summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/arm/aaci.c2
-rw-r--r--sound/arm/pxa2xx-ac97.c1
-rw-r--r--sound/core/Kconfig9
-rw-r--r--sound/core/init.c55
-rw-r--r--sound/core/pcm_lib.c3
-rw-r--r--sound/core/pcm_native.c40
-rw-r--r--sound/core/vmaster.c65
-rw-r--r--sound/drivers/aloop.c1
-rw-r--r--sound/drivers/dummy.c1
-rw-r--r--sound/drivers/ml403-ac97cr.c1
-rw-r--r--sound/drivers/mpu401/mpu401.c1
-rw-r--r--sound/drivers/mtpav.c1
-rw-r--r--sound/drivers/pcsp/pcsp.c1
-rw-r--r--sound/drivers/serial-u16550.c1
-rw-r--r--sound/drivers/virmidi.c1
-rw-r--r--sound/drivers/vx/vx_core.c2
-rw-r--r--sound/firewire/amdtp.h1
-rw-r--r--sound/firewire/scs1x.c4
-rw-r--r--sound/i2c/other/ak4xxx-adda.c2
-rw-r--r--sound/isa/ad1848/ad1848.c1
-rw-r--r--sound/isa/adlib.c1
-rw-r--r--sound/isa/cmi8328.c1
-rw-r--r--sound/isa/cmi8330.c1
-rw-r--r--sound/isa/cs423x/cs4231.c1
-rw-r--r--sound/isa/cs423x/cs4236.c2
-rw-r--r--sound/isa/es1688/es1688.c1
-rw-r--r--sound/isa/es18xx.c2
-rw-r--r--sound/isa/galaxy/galaxy.c1
-rw-r--r--sound/isa/gus/gusclassic.c1
-rw-r--r--sound/isa/gus/gusextreme.c1
-rw-r--r--sound/isa/gus/gusmax.c1
-rw-r--r--sound/isa/gus/interwave.c1
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c1
-rw-r--r--sound/isa/opl3sa2.c2
-rw-r--r--sound/isa/opti9xx/miro.c1
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c1
-rw-r--r--sound/isa/sb/jazz16.c1
-rw-r--r--sound/isa/sb/sb16.c1
-rw-r--r--sound/isa/sb/sb8.c1
-rw-r--r--sound/isa/sc6000.c1
-rw-r--r--sound/isa/sscape.c1
-rw-r--r--sound/isa/wavefront/wavefront.c1
-rw-r--r--sound/oss/kahlua.c2
-rw-r--r--sound/parisc/harmony.c3
-rw-r--r--sound/pci/ac97/ac97_codec.c2
-rw-r--r--sound/pci/ad1889.c1
-rw-r--r--sound/pci/ali5451/ali5451.c1
-rw-r--r--sound/pci/als300.c1
-rw-r--r--sound/pci/als4000.c1
-rw-r--r--sound/pci/asihpi/asihpi.c2
-rw-r--r--sound/pci/asihpi/hpioctl.c1
-rw-r--r--sound/pci/atiixp.c1
-rw-r--r--sound/pci/atiixp_modem.c1
-rw-r--r--sound/pci/au88x0/au88x0.c1
-rw-r--r--sound/pci/aw2/aw2-alsa.c1
-rw-r--r--sound/pci/azt3328.c1
-rw-r--r--sound/pci/bt87x.c1
-rw-r--r--sound/pci/ca0106/ca0106_main.c1
-rw-r--r--sound/pci/cmipci.c1
-rw-r--r--sound/pci/cs4281.c3
-rw-r--r--sound/pci/cs46xx/cs46xx.c1
-rw-r--r--sound/pci/cs5530.c1
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c1
-rw-r--r--sound/pci/ctxfi/xfi.c1
-rw-r--r--sound/pci/echoaudio/echoaudio.c1
-rw-r--r--sound/pci/emu10k1/emu10k1.c1
-rw-r--r--sound/pci/emu10k1/emu10k1x.c1
-rw-r--r--sound/pci/ens1370.c3
-rw-r--r--sound/pci/es1938.c1
-rw-r--r--sound/pci/es1968.c74
-rw-r--r--sound/pci/fm801.c1
-rw-r--r--sound/pci/hda/Kconfig1
-rw-r--r--sound/pci/hda/hda_codec.c69
-rw-r--r--sound/pci/hda/hda_codec.h11
-rw-r--r--sound/pci/hda/hda_generic.c59
-rw-r--r--sound/pci/hda/hda_generic.h4
-rw-r--r--sound/pci/hda/hda_intel.c53
-rw-r--r--sound/pci/hda/hda_jack.c2
-rw-r--r--sound/pci/hda/hda_local.h10
-rw-r--r--sound/pci/hda/hda_proc.c15
-rw-r--r--sound/pci/hda/patch_ca0132.c8
-rw-r--r--sound/pci/hda/patch_conexant.c2
-rw-r--r--sound/pci/hda/patch_hdmi.c128
-rw-r--r--sound/pci/hda/patch_realtek.c117
-rw-r--r--sound/pci/hda/patch_sigmatel.c14
-rw-r--r--sound/pci/hda/patch_via.c15
-rw-r--r--sound/pci/ice1712/ice1712.c1
-rw-r--r--sound/pci/ice1712/ice1724.c1
-rw-r--r--sound/pci/intel8x0.c1
-rw-r--r--sound/pci/intel8x0m.c1
-rw-r--r--sound/pci/korg1212/korg1212.c1
-rw-r--r--sound/pci/lola/lola.c1
-rw-r--r--sound/pci/lx6464es/lx6464es.c1
-rw-r--r--sound/pci/maestro3.c1
-rw-r--r--sound/pci/mixart/mixart.c1
-rw-r--r--sound/pci/nm256/nm256.c1
-rw-r--r--sound/pci/oxygen/oxygen_lib.c1
-rw-r--r--sound/pci/pcxhr/pcxhr.c1
-rw-r--r--sound/pci/riptide/riptide.c2
-rw-r--r--sound/pci/rme32.c1
-rw-r--r--sound/pci/rme96.c1
-rw-r--r--sound/pci/rme9652/hdsp.c1
-rw-r--r--sound/pci/rme9652/hdspm.c59
-rw-r--r--sound/pci/rme9652/rme9652.c1
-rw-r--r--sound/pci/sis7019.c1
-rw-r--r--sound/pci/sonicvibes.c1
-rw-r--r--sound/pci/trident/trident.c1
-rw-r--r--sound/pci/via82xx.c1
-rw-r--r--sound/pci/via82xx_modem.c1
-rw-r--r--sound/pci/vx222/vx222.c1
-rw-r--r--sound/pci/ymfpci/ymfpci.c1
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c2
-rw-r--r--sound/ppc/powermac.c1
-rw-r--r--sound/sh/aica.c1
-rw-r--r--sound/sh/sh_dac_audio.c2
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c9
-rw-r--r--sound/soc/au1x/ac97c.c21
-rw-r--r--sound/soc/au1x/psc-ac97.c33
-rw-r--r--sound/soc/blackfin/Kconfig47
-rw-r--r--sound/soc/blackfin/Makefile4
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c1
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h26
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c37
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c19
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c40
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c1
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c1
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c183
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h21
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c129
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c10
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h2
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c1
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c345
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.h18
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c328
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h23
-rw-r--r--sound/soc/cirrus/Kconfig2
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c28
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c16
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c138
-rw-r--r--sound/soc/codecs/88pm860x-codec.c15
-rw-r--r--sound/soc/codecs/Kconfig18
-rw-r--r--sound/soc/codecs/Makefile14
-rw-r--r--sound/soc/codecs/ab8500-codec.c85
-rw-r--r--sound/soc/codecs/ab8500-codec.h42
-rw-r--r--sound/soc/codecs/ac97.c7
-rw-r--r--sound/soc/codecs/ad1980.c12
-rw-r--r--sound/soc/codecs/adau1701.c321
-rw-r--r--sound/soc/codecs/arizona.c7
-rw-r--r--sound/soc/codecs/arizona.h3
-rw-r--r--sound/soc/codecs/bt-sco.c (renamed from sound/soc/codecs/dfbmcs320.c)37
-rw-r--r--sound/soc/codecs/hdmi.c (renamed from sound/soc/codecs/omap-hdmi.c)26
-rw-r--r--sound/soc/codecs/jz4740.c2
-rw-r--r--sound/soc/codecs/max98090.c24
-rw-r--r--sound/soc/codecs/rt5640.c2128
-rw-r--r--sound/soc/codecs/rt5640.h2092
-rw-r--r--sound/soc/codecs/sgtl5000.c267
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/si476x.c6
-rw-r--r--sound/soc/codecs/sn95031.c2
-rw-r--r--sound/soc/codecs/spdif_receiver.c10
-rw-r--r--sound/soc/codecs/spdif_transmitter.c (renamed from sound/soc/codecs/spdif_transciever.c)10
-rw-r--r--sound/soc/codecs/ssm2518.c856
-rw-r--r--sound/soc/codecs/ssm2518.h20
-rw-r--r--sound/soc/codecs/stac9766.c26
-rw-r--r--sound/soc/codecs/tas5086.c330
-rw-r--r--sound/soc/codecs/tlv320aic3x.c6
-rw-r--r--sound/soc/codecs/twl6040.c109
-rw-r--r--sound/soc/codecs/wm0010.c10
-rw-r--r--sound/soc/codecs/wm5102.c205
-rw-r--r--sound/soc/codecs/wm5110.c192
-rw-r--r--sound/soc/codecs/wm8400.c9
-rw-r--r--sound/soc/codecs/wm8903.c6
-rw-r--r--sound/soc/codecs/wm8904.c9
-rw-r--r--sound/soc/codecs/wm8962.c143
-rw-r--r--sound/soc/codecs/wm8990.c11
-rw-r--r--sound/soc/codecs/wm8991.h9
-rw-r--r--sound/soc/codecs/wm8994.c200
-rw-r--r--sound/soc/codecs/wm8994.h3
-rw-r--r--sound/soc/codecs/wm8995.h7
-rw-r--r--sound/soc/codecs/wm9705.c16
-rw-r--r--sound/soc/codecs/wm9712.c18
-rw-r--r--sound/soc/codecs/wm9713.c18
-rw-r--r--sound/soc/codecs/wm_adsp.c461
-rw-r--r--sound/soc/codecs/wm_adsp.h13
-rw-r--r--sound/soc/codecs/wm_hubs.c6
-rw-r--r--sound/soc/davinci/Kconfig10
-rw-r--r--sound/soc/davinci/Makefile2
-rw-r--r--sound/soc/davinci/davinci-evm.c1
-rw-r--r--sound/soc/davinci/davinci-mcasp.c4
-rw-r--r--sound/soc/davinci/davinci-pcm.c1
-rw-r--r--sound/soc/davinci/davinci-pcm.h2
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c181
-rw-r--r--sound/soc/dwc/designware_i2s.c6
-rw-r--r--sound/soc/fsl/Kconfig17
-rw-r--r--sound/soc/fsl/Makefile13
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c2
-rw-r--r--sound/soc/fsl/fsl_ssi.c13
-rw-r--r--sound/soc/fsl/imx-audmux.c8
-rw-r--r--sound/soc/fsl/imx-mc13783.c2
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c2
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c92
-rw-r--r--sound/soc/fsl/imx-pcm.c145
-rw-r--r--sound/soc/fsl/imx-pcm.h10
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c39
-rw-r--r--sound/soc/fsl/imx-ssi.c55
-rw-r--r--sound/soc/fsl/imx-ssi.h3
-rw-r--r--sound/soc/fsl/imx-wm8962.c323
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c10
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c2
-rw-r--r--sound/soc/fsl/phycore-ac97.c2
-rw-r--r--sound/soc/fsl/wm1133-ev1.c2
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c17
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c2
-rw-r--r--sound/soc/mid-x86/mfld_machine.c32
-rw-r--r--sound/soc/mxs/mxs-pcm.c18
-rw-r--r--sound/soc/mxs/mxs-pcm.h7
-rw-r--r--sound/soc/mxs/mxs-saif.c37
-rw-r--r--sound/soc/mxs/mxs-saif.h1
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c10
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c60
-rw-r--r--sound/soc/omap/Kconfig2
-rw-r--r--sound/soc/omap/Makefile1
-rw-r--r--sound/soc/omap/omap-hdmi-card.c2
-rw-r--r--sound/soc/omap/omap-mcbsp.c2
-rw-r--r--sound/soc/pxa/Kconfig20
-rw-r--r--sound/soc/pxa/Makefile4
-rw-r--r--sound/soc/pxa/mmp-pcm.c6
-rw-r--r--sound/soc/pxa/mmp-sspa.c2
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c10
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h3
-rw-r--r--sound/soc/pxa/saarb.c190
-rw-r--r--sound/soc/pxa/tavorevb3.c189
-rw-r--r--sound/soc/pxa/zylonite.c1
-rw-r--r--sound/soc/samsung/Kconfig2
-rw-r--r--sound/soc/samsung/ac97.c42
-rw-r--r--sound/soc/samsung/bells.c14
-rw-r--r--sound/soc/samsung/idma.c1
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c2
-rw-r--r--sound/soc/samsung/smdk_wm8580pcm.c1
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c1
-rw-r--r--sound/soc/sh/fsi.c10
-rw-r--r--sound/soc/sh/hac.c8
-rw-r--r--sound/soc/soc-core.c123
-rw-r--r--sound/soc/soc-dapm.c110
-rw-r--r--sound/soc/soc-pcm.c91
-rw-r--r--sound/soc/soc-utils.c13
-rw-r--r--sound/soc/spear/Kconfig9
-rw-r--r--sound/soc/spear/Makefile8
-rw-r--r--sound/soc/spear/spdif_in.c31
-rw-r--r--sound/soc/spear/spdif_out.c43
-rw-r--r--sound/soc/spear/spear_pcm.c152
-rw-r--r--sound/soc/tegra/Kconfig10
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra20_ac97.c67
-rw-r--r--sound/soc/tegra/tegra30_ahub.c25
-rw-r--r--sound/soc/tegra/tegra30_i2s.c26
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c23
-rw-r--r--sound/soc/tegra/tegra_rt5640.c257
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c17
-rw-r--r--sound/soc/ux500/mop500.c2
-rw-r--r--sound/soc/ux500/mop500_ab8500.c62
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c11
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h4
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c88
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h74
-rw-r--r--sound/soc/ux500/ux500_pcm.c40
-rw-r--r--sound/sound_core.c2
-rw-r--r--sound/sparc/dbri.c2
-rw-r--r--sound/spi/at73c213.c1
-rw-r--r--sound/usb/6fire/chip.c2
-rw-r--r--sound/usb/6fire/pcm.c12
-rw-r--r--sound/usb/Kconfig31
-rw-r--r--sound/usb/Makefile2
-rw-r--r--sound/usb/caiaq/audio.c14
-rw-r--r--sound/usb/caiaq/device.c31
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/clock.c4
-rw-r--r--sound/usb/format.c34
-rw-r--r--sound/usb/format.h2
-rw-r--r--sound/usb/hiface/Makefile2
-rw-r--r--sound/usb/hiface/chip.c297
-rw-r--r--sound/usb/hiface/chip.h30
-rw-r--r--sound/usb/hiface/pcm.c621
-rw-r--r--sound/usb/hiface/pcm.h24
-rw-r--r--sound/usb/midi.c74
-rw-r--r--sound/usb/misc/ua101.c2
-rw-r--r--sound/usb/mixer_quirks.c212
-rw-r--r--sound/usb/pcm.c45
-rw-r--r--sound/usb/quirks-table.h509
-rw-r--r--sound/usb/quirks.c209
-rw-r--r--sound/usb/stream.c18
-rw-r--r--sound/usb/usbaudio.h2
-rw-r--r--sound/usb/usx2y/usbusx2y.c2
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c3
298 files changed, 11321 insertions, 4024 deletions
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index aa5d8034890b..1ca8dc2ccb89 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -1076,8 +1076,6 @@ static int aaci_remove(struct amba_device *dev)
{
struct snd_card *card = amba_get_drvdata(dev);
- amba_set_drvdata(dev, NULL);
-
if (card) {
struct aaci *aaci = card->private_data;
writel(0, aaci->base + AACI_MAINCR);
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index ec54be4efff0..ce431e6e07cf 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -230,7 +230,6 @@ static int pxa2xx_ac97_remove(struct platform_device *dev)
if (card) {
snd_card_free(card);
- platform_set_drvdata(dev, NULL);
pxa2xx_ac97_hw_remove(dev);
}
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index b413ed05e74d..c0c2f57a0d6f 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -157,6 +157,15 @@ config SND_DYNAMIC_MINORS
If you are unsure about this, say N here.
+config SND_MAX_CARDS
+ int "Max number of sound cards"
+ range 4 256
+ default 32
+ depends on SND_DYNAMIC_MINORS
+ help
+ Specify the max number of sound cards that can be assigned
+ on a single machine.
+
config SND_SUPPORT_OLD_API
bool "Support old ALSA API"
default y
diff --git a/sound/core/init.c b/sound/core/init.c
index 6ef06400dfc8..6b9087115da2 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -46,7 +46,8 @@ static LIST_HEAD(shutdown_files);
static const struct file_operations snd_shutdown_f_ops;
-static unsigned int snd_cards_lock; /* locked for registering/using */
+/* locked for registering/using */
+static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
struct snd_card *snd_cards[SNDRV_CARDS];
EXPORT_SYMBOL(snd_cards);
@@ -167,29 +168,35 @@ int snd_card_create(int idx, const char *xid,
err = 0;
mutex_lock(&snd_card_mutex);
if (idx < 0) {
- for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
/* idx == -1 == 0xffff means: take any free slot */
- if (~snd_cards_lock & idx & 1<<idx2) {
+ if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
+ continue;
+ if (!test_bit(idx2, snd_cards_lock)) {
if (module_slot_match(module, idx2)) {
idx = idx2;
break;
}
}
+ }
}
if (idx < 0) {
- for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
/* idx == -1 == 0xffff means: take any free slot */
- if (~snd_cards_lock & idx & 1<<idx2) {
+ if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
+ continue;
+ if (!test_bit(idx2, snd_cards_lock)) {
if (!slots[idx2] || !*slots[idx2]) {
idx = idx2;
break;
}
}
+ }
}
if (idx < 0)
err = -ENODEV;
else if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
+ if (test_bit(idx, snd_cards_lock))
err = -EBUSY; /* invalid */
} else if (idx >= SNDRV_CARDS)
err = -ENODEV;
@@ -199,7 +206,7 @@ int snd_card_create(int idx, const char *xid,
idx, snd_ecards_limit - 1, err);
goto __error;
}
- snd_cards_lock |= 1 << idx; /* lock it */
+ set_bit(idx, snd_cards_lock); /* lock it */
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
@@ -249,7 +256,7 @@ int snd_card_locked(int card)
int locked;
mutex_lock(&snd_card_mutex);
- locked = snd_cards_lock & (1 << card);
+ locked = test_bit(card, snd_cards_lock);
mutex_unlock(&snd_card_mutex);
return locked;
}
@@ -361,7 +368,7 @@ int snd_card_disconnect(struct snd_card *card)
/* phase 1: disable fops (user space) operations for ALSA API */
mutex_lock(&snd_card_mutex);
snd_cards[card->number] = NULL;
- snd_cards_lock &= ~(1 << card->number);
+ clear_bit(card->number, snd_cards_lock);
mutex_unlock(&snd_card_mutex);
/* phase 2: replace file->f_op with special dummy operations */
@@ -549,7 +556,6 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
const char *nid)
{
int len, loops;
- bool with_suffix;
bool is_default = false;
char *id;
@@ -565,26 +571,23 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
is_default = true;
}
- with_suffix = false;
+ len = strlen(id);
for (loops = 0; loops < SNDRV_CARDS; loops++) {
+ char *spos;
+ char sfxstr[5]; /* "_012" */
+ int sfxlen;
+
if (card_id_ok(card, id))
return; /* OK */
- len = strlen(id);
- if (!with_suffix) {
- /* add the "_X" suffix */
- char *spos = id + len;
- if (len > sizeof(card->id) - 3)
- spos = id + sizeof(card->id) - 3;
- strcpy(spos, "_1");
- with_suffix = true;
- } else {
- /* modify the existing suffix */
- if (id[len - 1] != '9')
- id[len - 1]++;
- else
- id[len - 1] = 'A';
- }
+ /* Add _XYZ suffix */
+ sprintf(sfxstr, "_%X", loops + 1);
+ sfxlen = strlen(sfxstr);
+ if (len + sfxlen >= sizeof(card->id))
+ spos = id + sizeof(card->id) - sfxlen - 1;
+ else
+ spos = id + len;
+ strcpy(spos, sfxstr);
}
/* fallback to the default id */
if (!is_default) {
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 41b3dfe68698..82bb029d4414 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -568,7 +568,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
*
* Sets the given PCM operators to the pcm instance.
*/
-void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
+void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
+ const struct snd_pcm_ops *ops)
{
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index f92818155958..a68d4c6d702c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1589,29 +1589,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
}
-/* WARNING: Don't forget to fput back the file */
-static struct file *snd_pcm_file_fd(int fd, int *fput_needed)
+static bool is_pcm_file(struct file *file)
{
- struct file *file;
- struct inode *inode;
+ struct inode *inode = file_inode(file);
unsigned int minor;
- file = fget_light(fd, fput_needed);
- if (!file)
- return NULL;
- inode = file_inode(file);
- if (!S_ISCHR(inode->i_mode) ||
- imajor(inode) != snd_major) {
- fput_light(file, *fput_needed);
- return NULL;
- }
+ if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
+ return false;
minor = iminor(inode);
- if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) &&
- !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) {
- fput_light(file, *fput_needed);
- return NULL;
- }
- return file;
+ return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) ||
+ snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
}
/*
@@ -1620,16 +1607,18 @@ static struct file *snd_pcm_file_fd(int fd, int *fput_needed)
static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
{
int res = 0;
- struct file *file;
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1;
struct snd_pcm_group *group;
- int fput_needed;
+ struct fd f = fdget(fd);
- file = snd_pcm_file_fd(fd, &fput_needed);
- if (!file)
+ if (!f.file)
return -EBADFD;
- pcm_file = file->private_data;
+ if (!is_pcm_file(f.file)) {
+ res = -EBADFD;
+ goto _badf;
+ }
+ pcm_file = f.file->private_data;
substream1 = pcm_file->substream;
group = kmalloc(sizeof(*group), GFP_KERNEL);
if (!group) {
@@ -1663,8 +1652,9 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
up_write(&snd_pcm_link_rwsem);
_nolock:
snd_card_unref(substream1->pcm->card);
- fput_light(file, fput_needed);
kfree(group);
+ _badf:
+ fdput(f);
return res;
}
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 02f90b4f8b86..842a97d5fc3a 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -310,20 +310,10 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int master_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int sync_slaves(struct link_master *master, int old_val, int new_val)
{
- struct link_master *master = snd_kcontrol_chip(kcontrol);
struct link_slave *slave;
struct snd_ctl_elem_value *uval;
- int err, old_val;
-
- err = master_init(master);
- if (err < 0)
- return err;
- old_val = master->val;
- if (ucontrol->value.integer.value[0] == old_val)
- return 0;
uval = kmalloc(sizeof(*uval), GFP_KERNEL);
if (!uval)
@@ -332,11 +322,33 @@ static int master_put(struct snd_kcontrol *kcontrol,
master->val = old_val;
uval->id = slave->slave.id;
slave_get_val(slave, uval);
- master->val = ucontrol->value.integer.value[0];
+ master->val = new_val;
slave_put_val(slave, uval);
}
kfree(uval);
- if (master->hook && !err)
+ return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err, new_val, old_val;
+ bool first_init;
+
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ first_init = err;
+ old_val = master->val;
+ new_val = ucontrol->value.integer.value[0];
+ if (new_val == old_val)
+ return 0;
+
+ err = sync_slaves(master, old_val, new_val);
+ if (err < 0)
+ return err;
+ if (master->hook && !first_init)
master->hook(master->hook_private_data, master->val);
return 1;
}
@@ -442,20 +454,33 @@ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
/**
- * snd_ctl_sync_vmaster_hook - Sync the vmaster hook
+ * snd_ctl_sync_vmaster - Sync the vmaster slaves and hook
* @kcontrol: vmaster kctl element
+ * @hook_only: sync only the hook
*
- * Call the hook function to synchronize with the current value of the given
- * vmaster element. NOP when NULL is passed to @kcontrol or the hook doesn't
- * exist.
+ * Forcibly call the put callback of each slave and call the hook function
+ * to synchronize with the current value of the given vmaster element.
+ * NOP when NULL is passed to @kcontrol.
*/
-void snd_ctl_sync_vmaster_hook(struct snd_kcontrol *kcontrol)
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
{
struct link_master *master;
+ bool first_init = false;
+
if (!kcontrol)
return;
master = snd_kcontrol_chip(kcontrol);
- if (master->hook)
+ if (!hook_only) {
+ int err = master_init(master);
+ if (err < 0)
+ return;
+ first_init = err;
+ err = sync_slaves(master, master->val, master->val);
+ if (err < 0)
+ return;
+ }
+
+ if (master->hook && !first_init)
master->hook(master->hook_private_data, master->val);
}
-EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster_hook);
+EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 6f78de9c6fb6..f7589923effa 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1183,7 +1183,6 @@ static int loopback_probe(struct platform_device *devptr)
static int loopback_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index fd798f753609..11048cc744d0 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1129,7 +1129,6 @@ static int snd_dummy_probe(struct platform_device *devptr)
static int snd_dummy_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
index 8125a7e95ee4..95ea4a153ea4 100644
--- a/sound/drivers/ml403-ac97cr.c
+++ b/sound/drivers/ml403-ac97cr.c
@@ -1325,7 +1325,6 @@ static int snd_ml403_ac97cr_probe(struct platform_device *pfdev)
static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
{
snd_card_free(platform_get_drvdata(pfdev));
- platform_set_drvdata(pfdev, NULL);
return 0;
}
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index da1a29bfc85d..90a3a7b38a2a 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -129,7 +129,6 @@ static int snd_mpu401_probe(struct platform_device *devptr)
static int snd_mpu401_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 9f1815b99a15..e5ec7eb27dec 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -749,7 +749,6 @@ static int snd_mtpav_probe(struct platform_device *dev)
static int snd_mtpav_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index 7a5fdb9b0afc..1c19cd7ad26e 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -189,7 +189,6 @@ static int pcsp_remove(struct platform_device *dev)
struct snd_pcsp *chip = platform_get_drvdata(dev);
alsa_card_pcsp_exit(chip);
pcspkr_input_remove(chip->input_dev);
- platform_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 7425dd8c1f09..e0bf5e77b43a 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -985,7 +985,6 @@ static int snd_serial_probe(struct platform_device *devptr)
static int snd_serial_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index cc4be88d7318..ace3879e8d96 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -132,7 +132,6 @@ static int snd_virmidi_probe(struct platform_device *devptr)
static int snd_virmidi_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index c39961c11401..83596891cde4 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -205,7 +205,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
if (size < 1)
return 0;
- if (snd_BUG_ON(size > SIZE_MAX_STATUS))
+ if (snd_BUG_ON(size >= SIZE_MAX_STATUS))
return -EINVAL;
for (i = 1; i <= size; i++) {
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index b680c5ef01d6..f6103d68c4b1 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -3,7 +3,6 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
-#include <linux/spinlock.h>
#include "packets-buffer.h"
/**
diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c
index 844a555c3b1e..b252c21b6d13 100644
--- a/sound/firewire/scs1x.c
+++ b/sound/firewire/scs1x.c
@@ -405,8 +405,10 @@ static int scs_probe(struct device *unit_dev)
scs->output_idle = true;
scs->buffer = kmalloc(HSS1394_MAX_PACKET_SIZE, GFP_KERNEL);
- if (!scs->buffer)
+ if (!scs->buffer) {
+ err = -ENOMEM;
goto err_card;
+ }
scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
scs->hss_handler.address_callback = handle_hss;
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index cef813d23641..ed726d1569e8 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -571,7 +571,7 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
const char **input_names;
- int num_names, idx;
+ unsigned int num_names, idx;
num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
if (!num_names)
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index c214ecf45400..e3f455bd85cd 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -135,7 +135,6 @@ out: snd_card_free(card);
static int snd_ad1848_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index d26545543732..35659218710f 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -101,7 +101,6 @@ out: snd_card_free(card);
static int snd_adlib_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
index a7369fe19a6f..f84f073fc1e8 100644
--- a/sound/isa/cmi8328.c
+++ b/sound/isa/cmi8328.c
@@ -418,7 +418,6 @@ static int snd_cmi8328_remove(struct device *pdev, unsigned int dev)
snd_cmi8328_cfg_write(cmi->port, CFG2, 0);
snd_cmi8328_cfg_write(cmi->port, CFG3, 0);
snd_card_free(card);
- dev_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index c707c52268ab..270b9659ef7f 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -651,7 +651,6 @@ static int snd_cmi8330_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index aa7a5d86e480..ba9a74eff3e0 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -151,7 +151,6 @@ out: snd_card_free(card);
static int snd_cs4231_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 252e9fb37db3..69614acb2052 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -504,7 +504,6 @@ static int snd_cs423x_isa_remove(struct device *pdev,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
return 0;
}
@@ -600,7 +599,6 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
static void snd_cs423x_pnp_remove(struct pnp_dev *pdev)
{
snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 102874a703d4..cdcfb57f1f0a 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -213,7 +213,6 @@ out:
static int snd_es1688_isa_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 24380efe31a1..12978b864c3a 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -2235,7 +2235,6 @@ static int snd_es18xx_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
@@ -2305,7 +2304,6 @@ static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
static void snd_audiodrive_pnp_remove(struct pnp_dev *pdev)
{
snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index 672184e3221a..81244e7cea5b 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/sound/isa/galaxy/galaxy.c
@@ -623,7 +623,6 @@ error:
static int snd_galaxy_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 16bca4e96c08..1adc1b924f39 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -215,7 +215,6 @@ out: snd_card_free(card);
static int snd_gusclassic_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 0b9c2426b49f..38e1e3260c24 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -344,7 +344,6 @@ out: snd_card_free(card);
static int snd_gusextreme_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
return 0;
}
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index c309a5d0e7e1..652d5d834620 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -357,7 +357,6 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
static int snd_gusmax_remove(struct device *devptr, unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 78bc5744e89a..9942691cc0ca 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -849,7 +849,6 @@ static int snd_interwave_isa_probe(struct device *pdev,
static int snd_interwave_isa_remove(struct device *devptr, unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index ddabb406b14c..81aeb934261a 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -1064,7 +1064,6 @@ cfg_error:
static int snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
{
snd_msnd_unload(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 075777a6cf0b..cc01c419b7e9 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -757,7 +757,6 @@ static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
static void snd_opl3sa2_pnp_remove(struct pnp_dev *pdev)
{
snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
@@ -900,7 +899,6 @@ static int snd_opl3sa2_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index c3da1df9371d..619753d96ca5 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -1495,7 +1495,6 @@ static int snd_miro_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index b41ed8661b23..103b33373fd4 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -1035,7 +1035,6 @@ static int snd_opti9xx_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 4961da4e627c..356a6308392f 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -345,7 +345,6 @@ static int snd_jazz16_remove(struct device *devptr, unsigned int dev)
{
struct snd_card *card = dev_get_drvdata(devptr);
- dev_set_drvdata(devptr, NULL);
snd_card_free(card);
return 0;
}
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 50dbec454f98..a4130993955f 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -566,7 +566,6 @@ static int snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
static int snd_sb16_isa_remove(struct device *pdev, unsigned int dev)
{
snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 237d964ff8a6..a806ae90a944 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -208,7 +208,6 @@ static int snd_sb8_probe(struct device *pdev, unsigned int dev)
static int snd_sb8_remove(struct device *pdev, unsigned int dev)
{
snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 5376ebff845e..09d481b3ba7f 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -698,7 +698,6 @@ static int snd_sc6000_remove(struct device *devptr, unsigned int dev)
release_region(port[dev], 0x10);
release_region(mss_port[dev], 4);
- dev_set_drvdata(devptr, NULL);
snd_card_free(card);
return 0;
}
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 42a009720b29..57b338973ede 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1200,7 +1200,6 @@ _release_card:
static int snd_sscape_remove(struct device *devptr, unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index fe5dd982bd23..82dd76939fa0 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -581,7 +581,6 @@ static int snd_wavefront_isa_remove(struct device *devptr,
unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c
index 2a44cc106459..12be1fb512dd 100644
--- a/sound/oss/kahlua.c
+++ b/sound/oss/kahlua.c
@@ -178,7 +178,6 @@ static int probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
err_out_free:
- pci_set_drvdata(pdev, NULL);
kfree(hw_config);
return 1;
}
@@ -187,7 +186,6 @@ static void remove_one(struct pci_dev *pdev)
{
struct address_info *hw_config = pci_get_drvdata(pdev);
sb_dsp_unload(hw_config, 0);
- pci_set_drvdata(pdev, NULL);
kfree(hw_config);
}
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 0e66ba48d453..67f56a2cee6a 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -902,8 +902,6 @@ snd_harmony_free(struct snd_harmony *h)
if (h->iobase)
iounmap(h->iobase);
- parisc_set_drvdata(h->dev, NULL);
-
kfree(h);
return 0;
}
@@ -1016,7 +1014,6 @@ static int
snd_harmony_remove(struct parisc_device *padev)
{
snd_card_free(parisc_get_drvdata(padev));
- parisc_set_drvdata(padev, NULL);
return 0;
}
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index d37c683cfd7a..445ca481d8d3 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -1296,7 +1296,7 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
struct snd_ac97 *ac97)
{
int err;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
unsigned char lo_max, hi_max;
if (! snd_ac97_valid_reg(ac97, reg))
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index ad8a31173939..d2b9d617aee5 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -1046,7 +1046,6 @@ static void
snd_ad1889_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = {
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 53754f5edeb1..3dfa12b670eb 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2298,7 +2298,6 @@ static int snd_ali_probe(struct pci_dev *pci,
static void snd_ali_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver ali5451_driver = {
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 864c4310366b..591efb6eef05 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -282,7 +282,6 @@ static void snd_als300_remove(struct pci_dev *pci)
{
snd_als300_dbgcallenter();
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
snd_als300_dbgcallleave();
}
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 61efda2a4d94..ffc821b0139e 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -984,7 +984,6 @@ out:
static void snd_card_als4000_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index fbc17203613c..185d54a5cb1a 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1278,7 +1278,7 @@ struct hpi_control {
u16 dst_node_type;
u16 dst_node_index;
u16 band;
- char name[44]; /* copied to snd_ctl_elem_id.name[44]; */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */
};
static const char * const asihpi_tuner_band_names[] = {
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index ef5019fe5193..7f0272032fbb 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -445,7 +445,6 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
if (pa->p_buffer)
vfree(pa->p_buffer);
- pci_set_drvdata(pci_dev, NULL);
if (1)
dev_info(&pci_dev->dev,
"remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n",
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 6e78c6789858..fe4c61bdb8ba 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1714,7 +1714,6 @@ static int snd_atiixp_probe(struct pci_dev *pci,
static void snd_atiixp_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver atiixp_driver = {
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index d0bec7ba3b0d..cf29b9a1d65d 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1334,7 +1334,6 @@ static int snd_atiixp_probe(struct pci_dev *pci,
static void snd_atiixp_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver atiixp_modem_driver = {
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index b157e1fadd8f..7059dd69e5e6 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -371,7 +371,6 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
static void snd_vortex_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
// pci_driver definition
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 08e9a4702cbc..2925220d3fcf 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -392,7 +392,6 @@ static int snd_aw2_probe(struct pci_dev *pci,
static void snd_aw2_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
/* open callback */
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 1204a0fa3368..c8e121611593 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2725,7 +2725,6 @@ snd_azf3328_remove(struct pci_dev *pci)
{
snd_azf3328_dbgcallenter();
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
snd_azf3328_dbgcallleave();
}
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 9febe5509748..18802039497a 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -953,7 +953,6 @@ _error:
static void snd_bt87x_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
/* default entries for all Bt87x cards - it's not exported */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 1610a5705970..f4db5587e86e 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1896,7 +1896,6 @@ static int snd_ca0106_probe(struct pci_dev *pci,
static void snd_ca0106_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index c617435db6e6..2755ec5bcc25 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -3317,7 +3317,6 @@ static int snd_cmipci_probe(struct pci_dev *pci,
static void snd_cmipci_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 6a8695069941..1dc793e742d7 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1312,7 +1312,7 @@ static int snd_cs4281_free(struct cs4281 *chip)
/* Sound System Power Management - Turn Everything OFF */
snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
/* PCI interface - D3 state */
- pci_set_power_state(chip->pci, 3);
+ pci_set_power_state(chip->pci, PCI_D3hot);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
@@ -1971,7 +1971,6 @@ static int snd_cs4281_probe(struct pci_dev *pci,
static void snd_cs4281_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
/*
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 6b0d8b50a305..b03498325d66 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -158,7 +158,6 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci,
static void snd_card_cs46xx_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver cs46xx_driver = {
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index dace827b45d1..c6b82c85e044 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -91,7 +91,6 @@ static int snd_cs5530_dev_free(struct snd_device *device)
static void snd_cs5530_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg)
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 7e4b13e2d12a..902bebd3b3fb 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -391,7 +391,6 @@ static void snd_cs5535audio_remove(struct pci_dev *pci)
{
olpc_quirks_cleanup();
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver cs5535audio_driver = {
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index d01ffcb2b2f5..d464ad2fc7b7 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -122,7 +122,6 @@ error:
static void ct_card_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 760cbff53210..05cfe551ce42 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2323,7 +2323,6 @@ static void snd_echo_remove(struct pci_dev *pci)
chip = pci_get_drvdata(pci);
if (chip)
snd_card_free(chip->card);
- pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 8c5010f7889c..9e1bd0c39a8c 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -202,7 +202,6 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
static void snd_card_emu10k1_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index cdff11d48ebd..56ad9d6f200d 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1623,7 +1623,6 @@ static int snd_emu10k1x_probe(struct pci_dev *pci,
static void snd_emu10k1x_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
// PCI IDs
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index db2dc835171d..ca8929b9a5d6 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1939,7 +1939,7 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq)
#endif
if (ensoniq->irq >= 0)
synchronize_irq(ensoniq->irq);
- pci_set_power_state(ensoniq->pci, 3);
+ pci_set_power_state(ensoniq->pci, PCI_D3hot);
__hw_end:
#ifdef CHIP1370
if (ensoniq->dma_bug.area)
@@ -2497,7 +2497,6 @@ static int snd_audiopci_probe(struct pci_dev *pci,
static void snd_audiopci_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver ens137x_driver = {
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 8423403954ab..9213fb38921c 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1881,7 +1881,6 @@ static int snd_es1938_probe(struct pci_dev *pci,
static void snd_es1938_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver es1938_driver = {
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index a1f32b5ae0d1..5e2ec9687731 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -564,6 +564,7 @@ struct es1968 {
#ifdef CONFIG_SND_ES1968_RADIO
struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
+ unsigned int tea575x_tuner;
#endif
};
@@ -2557,37 +2558,47 @@ static int snd_es1968_input_register(struct es1968 *chip)
bits 1=unmask write to given bit */
#define IO_DIR 8 /* direction register offset from GPIO_DATA
bits 0/1=read/write direction */
-/* mask bits for GPIO lines */
-#define STR_DATA 0x0040 /* GPIO6 */
-#define STR_CLK 0x0080 /* GPIO7 */
-#define STR_WREN 0x0100 /* GPIO8 */
-#define STR_MOST 0x0200 /* GPIO9 */
+
+/* GPIO to TEA575x maps */
+struct snd_es1968_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
+
+static struct snd_es1968_tea575x_gpio snd_es1968_tea575x_gpios[] = {
+ { .data = 6, .clk = 7, .wren = 8, .most = 9, .name = "SF64-PCE2" },
+ { .data = 7, .clk = 8, .wren = 6, .most = 10, .name = "M56VAP" },
+};
+
+#define get_tea575x_gpio(chip) \
+ (&snd_es1968_tea575x_gpios[(chip)->tea575x_tuner])
+
static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct es1968 *chip = tea->private_data;
- unsigned long io = chip->io_port + GPIO_DATA;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
u16 val = 0;
- val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
- val |= (pins & TEA575X_CLK) ? STR_CLK : 0;
- val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
+ val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0;
+ val |= (pins & TEA575X_CLK) ? (1 << gpio.clk) : 0;
+ val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0;
- outw(val, io);
+ outw(val, chip->io_port + GPIO_DATA);
}
static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
{
struct es1968 *chip = tea->private_data;
- unsigned long io = chip->io_port + GPIO_DATA;
- u16 val = inw(io);
- u8 ret;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u16 val = inw(chip->io_port + GPIO_DATA);
+ u8 ret = 0;
- ret = 0;
- if (val & STR_DATA)
+ if (val & (1 << gpio.data))
ret |= TEA575X_DATA;
- if (val & STR_MOST)
+ if (val & (1 << gpio.most))
ret |= TEA575X_MOST;
+
return ret;
}
@@ -2596,13 +2607,18 @@ static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool outpu
struct es1968 *chip = tea->private_data;
unsigned long io = chip->io_port + GPIO_DATA;
u16 odir = inw(io + IO_DIR);
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
if (output) {
- outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
- outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
+ outw(~((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren)),
+ io + IO_MASK);
+ outw(odir | (1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren),
+ io + IO_DIR);
} else {
- outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
- outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
+ outw(~((1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) | (1 << gpio.most)),
+ io + IO_MASK);
+ outw((odir & ~((1 << gpio.data) | (1 << gpio.most)))
+ | (1 << gpio.clk) | (1 << gpio.wren), io + IO_DIR);
}
}
@@ -2772,6 +2788,9 @@ static int snd_es1968_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
#ifdef CONFIG_SND_ES1968_RADIO
+ /* don't play with GPIOs on laptops */
+ if (chip->pci->subsystem_vendor != 0x125d)
+ goto no_radio;
err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
if (err < 0) {
snd_es1968_free(chip);
@@ -2781,10 +2800,18 @@ static int snd_es1968_create(struct snd_card *card,
chip->tea.private_data = chip;
chip->tea.radio_nr = radio_nr;
chip->tea.ops = &snd_es1968_tea_ops;
- strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
- if (!snd_tea575x_init(&chip->tea, THIS_MODULE))
- printk(KERN_INFO "es1968: detected TEA575x radio\n");
+ for (i = 0; i < ARRAY_SIZE(snd_es1968_tea575x_gpios); i++) {
+ chip->tea575x_tuner = i;
+ if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ snd_printk(KERN_INFO "es1968: detected TEA575x radio type %s\n",
+ get_tea575x_gpio(chip)->name);
+ strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ sizeof(chip->tea.card));
+ break;
+ }
+ }
+no_radio:
#endif
*chip_ret = chip;
@@ -2909,7 +2936,6 @@ static int snd_es1968_probe(struct pci_dev *pci,
static void snd_es1968_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver es1968_driver = {
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 4f07fda5adf2..706c5b67b708 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1370,7 +1370,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
static void snd_card_fm801_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index c5a872ca7703..59c5e9c03d53 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -140,7 +140,6 @@ config SND_HDA_CODEC_VIA
config SND_HDA_CODEC_HDMI
bool "Build HDMI/DisplayPort HD-audio codec support"
- select SND_DYNAMIC_MINORS
default y
help
Say Y here to include HDMI and DisplayPort HD-audio codec
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 55108b5fb291..8a005f0e5ca4 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -185,20 +185,19 @@ EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
* Compose a 32bit command word to be sent to the HD-audio controller
*/
static inline unsigned int
-make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm)
{
u32 val;
- if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
+ if ((codec->addr & ~0xf) || (nid & ~0x7f) ||
(verb & ~0xfff) || (parm & ~0xffff)) {
- printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
- codec->addr, direct, nid, verb, parm);
+ printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x\n",
+ codec->addr, nid, verb, parm);
return ~0;
}
val = (u32)codec->addr << 28;
- val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
val |= parm;
@@ -209,7 +208,7 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
* Send and receive a verb
*/
static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
- unsigned int *res)
+ int flags, unsigned int *res)
{
struct hda_bus *bus = codec->bus;
int err;
@@ -222,6 +221,8 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
again:
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
+ if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
+ bus->no_response_fallback = 1;
for (;;) {
trace_hda_send_cmd(codec, cmd);
err = bus->ops.command(bus, cmd);
@@ -234,6 +235,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
*res = bus->ops.get_response(bus, codec->addr);
trace_hda_get_response(codec, *res);
}
+ bus->no_response_fallback = 0;
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) {
@@ -255,7 +257,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -264,12 +266,12 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
* 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 direct,
+ int flags,
unsigned int verb, unsigned int parm)
{
- unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
+ unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm);
unsigned int res;
- if (codec_exec_verb(codec, cmd, &res))
+ if (codec_exec_verb(codec, cmd, flags, &res))
return -1;
return res;
}
@@ -279,7 +281,7 @@ EXPORT_SYMBOL_HDA(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
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -287,12 +289,12 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- 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 cmd = make_codec_cmd(codec, nid, direct, verb, parm);
+ unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm);
unsigned int res;
- return codec_exec_verb(codec, cmd,
+ return codec_exec_verb(codec, cmd, flags,
codec->bus->sync_write ? &res : NULL);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write);
@@ -2523,7 +2525,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
flush_workqueue(bus->workq);
#endif
snd_hda_ctls_clear(codec);
- /* relase PCMs */
+ /* release PCMs */
for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) {
snd_device_free(card, codec->pcm_info[i].pcm);
@@ -3582,7 +3584,7 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
* snd_hda_codec_write_cache - send a single command with caching
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -3591,7 +3593,7 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
+ int flags, unsigned int verb, unsigned int parm)
{
int err;
struct hda_cache_head *c;
@@ -3600,7 +3602,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
cache_only = codec->cached_write;
if (!cache_only) {
- err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+ err = snd_hda_codec_write(codec, nid, flags, verb, parm);
if (err < 0)
return err;
}
@@ -3624,7 +3626,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
* @codec: the HDA codec
* @nid: NID to send the command
- * @direct: direct flag
+ * @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
@@ -3635,7 +3637,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
+ int flags, unsigned int verb, unsigned int parm)
{
struct hda_cache_head *c;
u32 key;
@@ -3651,7 +3653,7 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
return 0;
}
mutex_unlock(&codec->bus->cmd_mutex);
- return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
+ return snd_hda_codec_write_cache(codec, nid, flags, verb, parm);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
@@ -3806,11 +3808,13 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;
int count;
unsigned int state;
+ int flags = 0;
/* this delay seems necessary to avoid click noise at power-down */
if (power_state == AC_PWRST_D3) {
/* transition time less than 10ms for power down */
msleep(codec->epss ? 10 : 100);
+ flags = HDA_RW_NO_RESPONSE_FALLBACK;
}
/* repeat power states setting at most 10 times*/
@@ -3819,7 +3823,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
codec->patch_ops.set_power_state(codec, fg,
power_state);
else {
- snd_hda_codec_read(codec, fg, 0,
+ snd_hda_codec_read(codec, fg, flags,
AC_VERB_SET_POWER_STATE,
power_state);
snd_hda_codec_set_power_to_all(codec, fg, power_state);
@@ -4461,12 +4465,13 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
/*
* get the empty PCM device number to assign
- *
- * note the max device number is limited by HDA_MAX_PCMS, currently 10
*/
-static int get_empty_pcm_device(struct hda_bus *bus, int type)
+static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
{
/* audio device indices; not linear to keep compatibility */
+ /* assigned to static slots up to dev#10; if more needed, assign
+ * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
+ */
static int audio_idx[HDA_PCM_NTYPES][5] = {
[HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
[HDA_PCM_TYPE_SPDIF] = { 1, -1 },
@@ -4480,18 +4485,28 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
return -EINVAL;
}
- for (i = 0; audio_idx[type][i] >= 0 ; i++)
+ for (i = 0; audio_idx[type][i] >= 0; i++) {
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ if (audio_idx[type][i] >= 8)
+ break;
+#endif
if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
return audio_idx[type][i];
+ }
+#ifdef CONFIG_SND_DYNAMIC_MINORS
/* non-fixed slots starting from 10 */
for (i = 10; i < 32; i++) {
if (!test_and_set_bit(i, bus->pcm_dev_bits))
return i;
}
+#endif
snd_printk(KERN_WARNING "Too many %s devices\n",
snd_hda_pcm_type_name[type]);
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ snd_printk(KERN_WARNING "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
+#endif
return -EAGAIN;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c93f9021f452..701c2e069b10 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -679,6 +679,7 @@ struct hda_bus {
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int power_keep_link_on:1; /* don't power off HDA link */
+ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
};
@@ -930,6 +931,8 @@ enum {
HDA_INPUT, HDA_OUTPUT
};
+/* snd_hda_codec_read/write optional flags */
+#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
/*
* constructors
@@ -945,9 +948,9 @@ 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,
- int direct,
+ int flags,
unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm);
#define snd_hda_param_read(codec, nid, param) \
snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
@@ -986,11 +989,11 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
/* cached write */
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
+ int flags, unsigned int verb, unsigned int parm);
void snd_hda_sequence_write_cache(struct hda_codec *codec,
const struct hda_verb *seq);
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
+ int flags, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec);
/* both for cmd & amp caches */
void snd_hda_codec_flush_cache(struct hda_codec *codec);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 4b1524a861f3..8e77cbbad871 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -133,6 +133,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
if (val >= 0)
spec->line_in_auto_switch = !!val;
+ val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
+ if (val >= 0)
+ spec->auto_mute_via_amp = !!val;
val = snd_hda_get_bool_hint(codec, "need_dac_fix");
if (val >= 0)
spec->need_dac_fix = !!val;
@@ -808,6 +811,9 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
* Helper functions for creating mixer ctl elements
*/
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
enum {
HDA_CTL_WIDGET_VOL,
HDA_CTL_WIDGET_MUTE,
@@ -815,7 +821,15 @@ enum {
};
static const struct snd_kcontrol_new control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ /* only the put callback is replaced for handling the special mute */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = hda_gen_mixer_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
HDA_BIND_MUTE(NULL, 0, 0, 0),
};
@@ -840,7 +854,7 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
const char *pfx, const char *dir,
const char *sfx, int cidx, unsigned long val)
{
- char name[32];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
if (!add_control(spec, type, name, cidx, val))
return -ENOMEM;
@@ -922,6 +936,23 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
return add_sw_ctl(codec, pfx, cidx, chs, path);
}
+/* playback mute control with the software mute bit check */
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->auto_mute_via_amp) {
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ bool enabled = !((spec->mute_bits >> nid) & 1);
+ ucontrol->value.integer.value[0] &= enabled;
+ ucontrol->value.integer.value[1] &= enabled;
+ }
+
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+
/* any ctl assigned to the path with the given index? */
static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
{
@@ -1900,7 +1931,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
for (i = 0; i < num_pins; i++) {
const char *name;
- char tmp[44];
+ char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int err, idx = 0;
if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
@@ -2453,7 +2484,7 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
}
if (get_out_jack_num_items(codec, pin) > 1) {
struct snd_kcontrol_new *knew;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
get_jack_mode_name(codec, pin, name, sizeof(name));
knew = snd_hda_gen_add_kctl(spec, name,
&out_jack_mode_enum);
@@ -2585,7 +2616,7 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
struct snd_kcontrol_new *knew;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
unsigned int defcfg;
if (pin == spec->hp_mic_pin)
@@ -3285,7 +3316,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
bool inv_dmic)
{
struct hda_gen_spec *spec = codec->spec;
- char tmpname[44];
+ char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
const char *sfx = is_switch ? "Switch" : "Volume";
unsigned int chs = inv_dmic ? 1 : 3;
@@ -3547,7 +3578,7 @@ static int parse_mic_boost(struct hda_codec *codec)
struct nid_path *path;
unsigned int val;
int idx;
- char boost_label[44];
+ char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
idx = imux->items[i].index;
if (idx >= imux->num_items)
@@ -3719,6 +3750,16 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
unsigned int val, oldval;
if (!nid)
break;
+
+ if (spec->auto_mute_via_amp) {
+ if (mute)
+ spec->mute_bits |= (1ULL << nid);
+ else
+ spec->mute_bits &= ~(1ULL << nid);
+ set_pin_eapd(codec, nid, !mute);
+ continue;
+ }
+
oldval = snd_hda_codec_get_pin_target(codec, nid);
if (oldval & PIN_IN)
continue; /* no mute for inputs */
@@ -3786,6 +3827,10 @@ static void call_update_outputs(struct hda_codec *codec)
spec->automute_hook(codec);
else
snd_hda_gen_update_outputs(codec);
+
+ /* sync the whole vmaster slaves to reflect the new auto-mute status */
+ if (spec->auto_mute_via_amp && !codec->bus->shutdown)
+ snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false);
}
/* standard HP-automute helper */
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 76200314ee95..e199a852388b 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -209,6 +209,7 @@ struct hda_gen_spec {
unsigned int master_mute:1; /* master mute over all */
unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
+ unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */
/* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
@@ -237,6 +238,9 @@ struct hda_gen_spec {
unsigned int have_aamix_ctl:1;
unsigned int hp_mic_jack_modes:1;
+ /* additional mute flags (only effective with auto_mute_via_amp=1) */
+ u64 mute_bits;
+
/* badness tables for output path evaluations */
const struct badness_table *main_out_badness;
const struct badness_table *extra_out_badness;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 35e9f8b010a7..8860dd529520 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -948,6 +948,9 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
}
}
+ if (!bus->no_response_fallback)
+ return -1;
+
if (!chip->polling_mode && chip->poll_count < 2) {
snd_printdd(SFX "%s: azx_get_response timeout, "
"polling the codec once: last cmd=0x%08x\n",
@@ -1123,37 +1126,52 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus,
struct snd_dma_buffer *dmab);
#endif
-/* reset codec link */
-static int azx_reset(struct azx *chip, int full_reset)
+/* enter link reset */
+static void azx_enter_link_reset(struct azx *chip)
{
unsigned long timeout;
- if (!full_reset)
- goto __skip;
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
/* reset controller */
azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
timeout = jiffies + msecs_to_jiffies(100);
- while (azx_readb(chip, GCTL) &&
+ while ((azx_readb(chip, GCTL) & ICH6_GCTL_RESET) &&
time_before(jiffies, timeout))
usleep_range(500, 1000);
+}
- /* delay for >= 100us for codec PLL to settle per spec
- * Rev 0.9 section 5.5.1
- */
- usleep_range(500, 1000);
+/* exit link reset */
+static void azx_exit_link_reset(struct azx *chip)
+{
+ unsigned long timeout;
- /* Bring controller out of reset */
azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
timeout = jiffies + msecs_to_jiffies(100);
while (!azx_readb(chip, GCTL) &&
time_before(jiffies, timeout))
usleep_range(500, 1000);
+}
+
+/* reset codec link */
+static int azx_reset(struct azx *chip, int full_reset)
+{
+ if (!full_reset)
+ goto __skip;
+
+ /* clear STATESTS */
+ azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+
+ /* reset controller */
+ azx_enter_link_reset(chip);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ usleep_range(500, 1000);
+
+ /* Bring controller out of reset */
+ azx_exit_link_reset(chip);
/* Brent Chartrand said to wait >= 540us for codecs to initialize */
usleep_range(1000, 1200);
@@ -2897,6 +2915,7 @@ static int azx_suspend(struct device *dev)
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
@@ -2953,6 +2972,7 @@ static int azx_runtime_suspend(struct device *dev)
struct azx *chip = card->private_data;
azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
azx_clear_irq_pending(chip);
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
hda_display_power(false);
@@ -3800,7 +3820,6 @@ static int azx_probe(struct pci_dev *pci,
out_free:
snd_card_free(card);
- pci_set_drvdata(pci, NULL);
return err;
}
@@ -3884,7 +3903,6 @@ static void azx_remove(struct pci_dev *pci)
if (card)
snd_card_free(card);
- pci_set_drvdata(pci, NULL);
}
/* PCI IDs */
@@ -3931,6 +3949,9 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* Oaktrail */
{ PCI_DEVICE(0x8086, 0x080a),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ /* BayTrail */
+ { PCI_DEVICE(0x8086, 0x0f04),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* ICH */
{ PCI_DEVICE(0x8086, 0x2668),
.driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 9e0a95288f46..3fd2973183e2 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -398,7 +398,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
const char *base_name)
{
unsigned int def_conf, conn;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int idx, err;
bool phantom_jack;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index e0bf7534fa1f..2e7493ef8ee0 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -562,6 +562,14 @@ static inline unsigned int get_wcaps_channels(u32 wcaps)
return chans;
}
+static inline void snd_hda_override_wcaps(struct hda_codec *codec,
+ hda_nid_t nid, u32 val)
+{
+ if (nid >= codec->start_nid &&
+ nid < codec->start_nid + codec->num_nodes)
+ codec->wcaps[nid - codec->start_nid] = val;
+}
+
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
@@ -667,7 +675,7 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
if (state & AC_PWRST_ERROR)
return true;
state = (state >> 4) & 0x0f;
- return (state != target_state);
+ return (state == target_state);
}
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 0fee8fae590a..9760f001916d 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -504,6 +504,8 @@ static void print_conn_list(struct snd_info_buffer *buffer,
int conn_len)
{
int c, curr = -1;
+ const hda_nid_t *list;
+ int cache_len;
if (conn_len > 1 &&
wid_type != AC_WID_AUD_MIX &&
@@ -521,6 +523,19 @@ static void print_conn_list(struct snd_info_buffer *buffer,
}
snd_iprintf(buffer, "\n");
}
+
+ /* Get Cache connections info */
+ cache_len = snd_hda_get_conn_list(codec, nid, &list);
+ if (cache_len != conn_len
+ || memcmp(list, conn, conn_len)) {
+ snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
+ if (cache_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < cache_len; c++)
+ snd_iprintf(buffer, " 0x%02x", list[c]);
+ snd_iprintf(buffer, "\n");
+ }
+ }
}
static void print_gpio(struct snd_info_buffer *buffer,
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 90ff7a3f72df..6e9876f27d95 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -139,7 +139,7 @@ enum {
#define DSP_SPEAKER_OUT_LATENCY 7
struct ct_effect {
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
hda_nid_t nid;
int mid; /*effect module ID*/
int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
@@ -270,7 +270,7 @@ enum {
};
struct ct_tuning_ctl {
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
hda_nid_t parent_nid;
hda_nid_t nid;
int mid; /*effect module ID*/
@@ -3103,7 +3103,7 @@ static int add_tuning_control(struct hda_codec *codec,
hda_nid_t pnid, hda_nid_t nid,
const char *name, int dir)
{
- char namestr[44];
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
@@ -3935,7 +3935,7 @@ static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
const char *pfx, int dir)
{
- char namestr[44];
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index b314d3e6d7fa..de00ce166470 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -2947,7 +2947,6 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
{}
@@ -3318,6 +3317,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index e12f7a030c58..540bdef2f904 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1018,13 +1018,18 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
hdmi_non_intrinsic_event(codec, res);
}
-static void haswell_verify_pin_D0(struct hda_codec *codec, hda_nid_t nid)
+static void haswell_verify_pin_D0(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t nid)
{
int pwr, lamp, ramp;
- pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
- pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
- if (pwr != AC_PWRST_D0) {
+ /* For Haswell, the converter 1/2 may keep in D3 state after bootup,
+ * thus pins could only choose converter 0 for use. Make sure the
+ * converters are in correct power state */
+ if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
+ snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
AC_PWRST_D0);
msleep(40);
@@ -1068,7 +1073,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
int new_pinctl = 0;
if (codec->vendor_id == 0x80862807)
- haswell_verify_pin_D0(codec, pin_nid);
+ haswell_verify_pin_D0(codec, cvt_nid, pin_nid);
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
@@ -1101,26 +1106,15 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
return 0;
}
-/*
- * HDA PCM callbacks
- */
-static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static int hdmi_choose_cvt(struct hda_codec *codec,
+ int pin_idx, int *cvt_id, int *mux_id)
{
struct hdmi_spec *spec = codec->spec;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int pin_idx, cvt_idx, mux_idx = 0;
struct hdmi_spec_per_pin *per_pin;
- struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int cvt_idx, mux_idx = 0;
- /* Validate hinfo */
- pin_idx = hinfo_to_pin_index(spec, hinfo);
- if (snd_BUG_ON(pin_idx < 0))
- return -EINVAL;
per_pin = get_pin(spec, pin_idx);
- eld = &per_pin->sink_eld;
/* Dynamically assign converter to stream */
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1138,17 +1132,89 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
continue;
break;
}
+
/* No free converters */
if (cvt_idx == spec->num_cvts)
return -ENODEV;
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+ if (mux_id)
+ *mux_id = mux_idx;
+
+ return 0;
+}
+
+static void haswell_config_cvts(struct hda_codec *codec,
+ int pin_id, int mux_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ int pin_idx, mux_idx;
+ int curr;
+ int err;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+
+ if (pin_idx == pin_id)
+ continue;
+
+ curr = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+
+ /* Choose another unused converter */
+ if (curr == mux_id) {
+ err = hdmi_choose_cvt(codec, pin_idx, NULL, &mux_idx);
+ if (err < 0)
+ return;
+ snd_printdd("HDMI: choose converter %d for pin %d\n", mux_idx, pin_idx);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ }
+ }
+}
+
+/*
+ * HDA PCM callbacks
+ */
+static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pin_idx, cvt_idx, mux_idx = 0;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ /* Validate hinfo */
+ pin_idx = hinfo_to_pin_index(spec, hinfo);
+ if (snd_BUG_ON(pin_idx < 0))
+ return -EINVAL;
+ per_pin = get_pin(spec, pin_idx);
+ eld = &per_pin->sink_eld;
+
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
+ if (err < 0)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
/* Claim converter */
per_cvt->assigned = 1;
hinfo->nid = per_cvt->cvt_nid;
- snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL,
mux_idx);
+
+ /* configure unused pins to choose other converters */
+ if (codec->vendor_id == 0x80862807)
+ haswell_config_cvts(codec, pin_idx, mux_idx);
+
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
/* Initially set the converter's capabilities */
@@ -1798,12 +1864,33 @@ static void generic_hdmi_free(struct hda_codec *codec)
kfree(spec);
}
+#ifdef CONFIG_PM
+static int generic_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ generic_hdmi_init(codec);
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hdmi_present_sense(per_pin, 1);
+ }
+ return 0;
+}
+#endif
+
static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
.build_controls = generic_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
+#ifdef CONFIG_PM
+ .resume = generic_hdmi_resume,
+#endif
};
@@ -1821,7 +1908,6 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
/* override pins connection list */
snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
- nconns = max(spec->num_cvts, 4);
snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 403010c9e82e..14ac9b0e740c 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -115,6 +115,7 @@ struct alc_spec {
int init_amp;
int codec_variant; /* flag for other variants */
+ bool has_alc5505_dsp;
/* for PLL fix */
hda_nid_t pll_nid;
@@ -2580,7 +2581,96 @@ static void alc269_shutup(struct hda_codec *codec)
}
}
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
+{
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
+}
+
+static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
+{
+ unsigned int val;
+
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ & 0xffff;
+ val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ << 16;
+ return val;
+}
+
+static void alc5505_dsp_halt(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
+ alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
+ alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
+ alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
+ alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
+ alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
+ val = alc5505_coef_get(codec, 0x6220);
+ alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
+}
+
+static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
+{
+ alc5505_coef_set(codec, 0x61b8, 0x04133302);
+ alc5505_coef_set(codec, 0x61b0, 0x00005b16);
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
+ alc5505_coef_set(codec, 0x6230, 0xf80d4011);
+ alc5505_coef_set(codec, 0x6220, 0x2002010f);
+ alc5505_coef_set(codec, 0x880c, 0x00000004);
+}
+
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ alc5505_dsp_halt(codec);
+ alc5505_dsp_back_from_halt(codec);
+ alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
+ alc5505_coef_set(codec, 0x61b0, 0x5b16);
+ alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61b4, 0x04132b02);
+ alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
+ alc5505_coef_set(codec, 0x61b8, 0x041f3302);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
+ alc5505_coef_set(codec, 0x61b8, 0x041b3302);
+ alc5505_coef_set(codec, 0x61b8, 0x04173302);
+ alc5505_coef_set(codec, 0x61b8, 0x04163302);
+ alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
+ alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
+ alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */
+
+ val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
+ if (val <= 3)
+ alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
+ else
+ alc5505_coef_set(codec, 0x6220, 0x6002018f);
+
+ alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
+ alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
+ alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
+ alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
+ alc5505_coef_set(codec, 0x880c, 0x00000003);
+ alc5505_coef_set(codec, 0x880c, 0x00000010);
+}
+
#ifdef CONFIG_PM
+static int alc269_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_halt(codec);
+ return alc_suspend(codec);
+}
+
static int alc269_resume(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -2603,7 +2693,10 @@ static int alc269_resume(struct hda_codec *codec)
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+ alc_inv_dmic_sync(codec, true);
hda_call_check_power_status(codec, 0x01);
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_back_from_halt(codec);
return 0;
}
#endif /* CONFIG_PM */
@@ -3225,6 +3318,7 @@ enum {
ALC271_FIXUP_HP_GATE_MIC_JACK,
ALC269_FIXUP_ACER_AC700,
ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+ ALC269VB_FIXUP_ORDISSIMO_EVE2,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -3467,6 +3561,15 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_limit_int_mic_boost,
},
+ [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x18, 0x03a11d20 }, /* mic */
+ { 0x19, 0x411111f0 }, /* Unused bogus pin */
+ { }
+ },
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -3482,6 +3585,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05de, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05e0, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -3495,9 +3600,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05fb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -3539,6 +3649,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+ SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
#if 0
/* Below is a quirk table taken from the old code.
@@ -3718,6 +3829,11 @@ static int patch_alc269(struct hda_codec *codec)
break;
}
+ if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = true;
+ spec->init_hook = alc5505_dsp_init;
+ }
+
/* automatic parse from the BIOS config */
err = alc269_parse_auto_config(codec);
if (err < 0)
@@ -3728,6 +3844,7 @@ static int patch_alc269(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM
+ codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume;
#endif
spec->shutup = alc269_shutup;
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 1d9d6427e0bf..e2f83591161b 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -2233,6 +2233,10 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
"HP Folio", STAC_92HD83XXX_HP_MIC_LED),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
"HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
@@ -3707,14 +3711,6 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#endif
#ifdef CONFIG_PM
-static int stac_resume(struct hda_codec *codec)
-{
- codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- return 0;
-}
-
static int stac_suspend(struct hda_codec *codec)
{
stac_shutup(codec);
@@ -3743,7 +3739,6 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg,
}
#else
#define stac_suspend NULL
-#define stac_resume NULL
#define stac_set_power_state NULL
#endif /* CONFIG_PM */
@@ -3755,7 +3750,6 @@ static const struct hda_codec_ops stac_patch_ops = {
.unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.suspend = stac_suspend,
- .resume = stac_resume,
#endif
.reboot_notify = stac_shutup,
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index e5245544eb52..e2481baddc70 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -480,14 +480,9 @@ static int via_suspend(struct hda_codec *codec)
struct via_spec *spec = codec->spec;
vt1708_stop_hp_work(codec);
- if (spec->codec_type == VT1802) {
- /* Fix pop noise on headphones */
- int i;
- for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
- snd_hda_codec_write(codec, spec->gen.autocfg.hp_pins[i],
- 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- 0x00);
- }
+ /* Fix pop noise on headphones */
+ if (spec->codec_type == VT1802)
+ snd_hda_shutup_pins(codec);
return 0;
}
@@ -746,6 +741,8 @@ static int patch_vt1708(struct hda_codec *codec)
/* don't support the input jack switching due to lack of unsol event */
/* (it may work with polling, though, but it needs testing) */
spec->gen.suppress_auto_mic = 1;
+ /* Some machines show the broken speaker mute */
+ spec->gen.auto_mute_via_amp = 1;
/* Add HP and CD pin config connect bit re-config action */
vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
@@ -910,6 +907,8 @@ static const struct hda_verb vt1708S_init_verbs[] = {
static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
int offset, int num_steps, int step_size)
{
+ snd_hda_override_wcaps(codec, pin,
+ get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
(offset << AC_AMPCAP_OFFSET_SHIFT) |
(num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 806407a3973e..28ec872e54c0 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2807,7 +2807,6 @@ static void snd_ice1712_remove(struct pci_dev *pci)
if (ice->card_info && ice->card_info->chip_exit)
ice->card_info->chip_exit(ice);
snd_card_free(card);
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver ice1712_driver = {
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index ce70e7f113e0..500471778291 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2800,7 +2800,6 @@ static void snd_vt1724_remove(struct pci_dev *pci)
if (ice->card_info && ice->card_info->chip_exit)
ice->card_info->chip_exit(ice);
snd_card_free(card);
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index b8fe40531b9c..59c8aaebb91e 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -3364,7 +3364,6 @@ static int snd_intel8x0_probe(struct pci_dev *pci,
static void snd_intel8x0_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver intel8x0_driver = {
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index fea09e8ea608..3573c1193665 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1328,7 +1328,6 @@ static int snd_intel8x0m_probe(struct pci_dev *pci,
static void snd_intel8x0m_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver intel8x0m_driver = {
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 43b4228d9afe..9cf9829555d4 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2473,7 +2473,6 @@ snd_korg1212_probe(struct pci_dev *pci,
static void snd_korg1212_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver korg1212_driver = {
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
index 322b638e8ec4..7307d97186cb 100644
--- a/sound/pci/lola/lola.c
+++ b/sound/pci/lola/lola.c
@@ -759,7 +759,6 @@ out_free:
static void lola_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
/* PCI IDs */
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 298bc9b72991..3230e57f246c 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -1139,7 +1139,6 @@ out_free:
static void snd_lx6464es_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index c76ac1411210..d5417360f51f 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2775,7 +2775,6 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
static void snd_m3_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver m3_driver = {
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 934dec98e2ce..1e0f6ee193f0 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1377,7 +1377,6 @@ static int snd_mixart_probe(struct pci_dev *pci,
static void snd_mixart_remove(struct pci_dev *pci)
{
snd_mixart_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver mixart_driver = {
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 6febedb05936..fe79fff4c6dc 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1746,7 +1746,6 @@ static int snd_nm256_probe(struct pci_dev *pci,
static void snd_nm256_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 9562dc63ba60..b0cb48adddc7 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -722,7 +722,6 @@ EXPORT_SYMBOL(oxygen_pci_probe);
void oxygen_pci_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
EXPORT_SYMBOL(oxygen_pci_remove);
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index b97384ad946d..d379b284955b 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1691,7 +1691,6 @@ static int pcxhr_probe(struct pci_dev *pci,
static void pcxhr_remove(struct pci_dev *pci)
{
pcxhr_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver pcxhr_driver = {
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 63c1c8041554..56cc891e395e 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -2066,7 +2066,6 @@ static void snd_riptide_joystick_remove(struct pci_dev *pci)
if (gameport) {
release_region(gameport->io, 8);
gameport_unregister_port(gameport);
- pci_set_drvdata(pci, NULL);
}
}
#endif
@@ -2179,7 +2178,6 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
static void snd_card_riptide_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver driver = {
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 0ecd4100713e..cc26346ae66b 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1981,7 +1981,6 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
static void snd_rme32_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver rme32_driver = {
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 5fb88ac82aa9..2a8ad9d1a2ae 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -2390,7 +2390,6 @@ snd_rme96_probe(struct pci_dev *pci,
static void snd_rme96_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver rme96_driver = {
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 94084cdb130c..4f255dfee450 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5412,7 +5412,6 @@ static int snd_hdsp_probe(struct pci_dev *pci,
static void snd_hdsp_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver hdsp_driver = {
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 9ea05e956474..bd501931ee23 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -400,8 +400,8 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_wc_freq0 (1<<5) /* input freq detected via autosync */
#define HDSPM_wc_freq1 (1<<6) /* 001=32, 010==44.1, 011=48, */
-#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */
-/* missing Bit for 111=128, 1000=176.4, 1001=192 */
+#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, 111=128 */
+#define HDSPM_wc_freq3 0x800 /* 1000=176.4, 1001=192 */
#define HDSPM_SyncRef0 0x10000 /* Sync Reference */
#define HDSPM_SyncRef1 0x20000
@@ -412,13 +412,17 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
-#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2|\
+ HDSPM_wc_freq3)
#define HDSPM_wcFreq32 (HDSPM_wc_freq0)
#define HDSPM_wcFreq44_1 (HDSPM_wc_freq1)
#define HDSPM_wcFreq48 (HDSPM_wc_freq0|HDSPM_wc_freq1)
#define HDSPM_wcFreq64 (HDSPM_wc_freq2)
#define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2)
#define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq128 (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq176_4 (HDSPM_wc_freq3)
+#define HDSPM_wcFreq192 (HDSPM_wc_freq0|HDSPM_wc_freq3)
#define HDSPM_status1_F_0 0x0400000
#define HDSPM_status1_F_1 0x0800000
@@ -1087,6 +1091,26 @@ static int hdspm_round_frequency(int rate)
return 48000;
}
+/* QS and DS rates normally can not be detected
+ * automatically by the card. Only exception is MADI
+ * in 96k frame mode.
+ *
+ * So if we read SS values (32 .. 48k), check for
+ * user-provided DS/QS bits in the control register
+ * and multiply the base frequency accordingly.
+ */
+static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
+{
+ if (rate <= 48000) {
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ return rate * 4;
+ else if (hdspm->control_register &
+ HDSPM_DoubleSpeed)
+ return rate * 2;
+ };
+ return rate;
+}
+
static int hdspm_tco_sync_check(struct hdspm *hdspm);
static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
@@ -1181,6 +1205,15 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
case HDSPM_wcFreq96:
rate = 96000;
break;
+ case HDSPM_wcFreq128:
+ rate = 128000;
+ break;
+ case HDSPM_wcFreq176_4:
+ rate = 176400;
+ break;
+ case HDSPM_wcFreq192:
+ rate = 192000;
+ break;
default:
rate = 0;
break;
@@ -1192,7 +1225,7 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
*/
if (rate != 0 &&
(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
- return rate;
+ return hdspm_rate_multiplier(hdspm, rate);
/* maybe a madi input (which is taken if sel sync is madi) */
if (status & HDSPM_madiLock) {
@@ -1255,21 +1288,8 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
}
}
- /* QS and DS rates normally can not be detected
- * automatically by the card. Only exception is MADI
- * in 96k frame mode.
- *
- * So if we read SS values (32 .. 48k), check for
- * user-provided DS/QS bits in the control register
- * and multiply the base frequency accordingly.
- */
- if (rate <= 48000) {
- if (hdspm->control_register & HDSPM_QuadSpeed)
- rate *= 4;
- else if (hdspm->control_register &
- HDSPM_DoubleSpeed)
- rate *= 2;
- }
+ rate = hdspm_rate_multiplier(hdspm, rate);
+
break;
}
@@ -6737,7 +6757,6 @@ static int snd_hdspm_probe(struct pci_dev *pci,
static void snd_hdspm_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver hdspm_driver = {
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 773a67fff4cd..b96d9e1adf6d 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2628,7 +2628,6 @@ static int snd_rme9652_probe(struct pci_dev *pci,
static void snd_rme9652_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver rme9652_driver = {
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 748e82d4d257..e413b4e2c819 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1482,7 +1482,6 @@ error_out:
static void snd_sis7019_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver sis7019_driver = {
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index a2e7686e7ae3..2a46bf98af30 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1528,7 +1528,6 @@ static int snd_sonic_probe(struct pci_dev *pci,
static void snd_sonic_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver sonicvibes_driver = {
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 1aefd6204a63..b3b588bc94c3 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -169,7 +169,6 @@ static int snd_trident_probe(struct pci_dev *pci,
static void snd_trident_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver trident_driver = {
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index d756a3562706..3c511d0caf9e 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -2646,7 +2646,6 @@ static int snd_via82xx_probe(struct pci_dev *pci,
static void snd_via82xx_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver via82xx_driver = {
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 4f5fd80b7e56..ca190283cbd7 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1227,7 +1227,6 @@ static int snd_via82xx_probe(struct pci_dev *pci,
static void snd_via82xx_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver via82xx_modem_driver = {
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index e2f1ab37e154..ab8a9b1bfb8e 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -254,7 +254,6 @@ static int snd_vx222_probe(struct pci_dev *pci,
static void snd_vx222_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 01c49655a3c1..e8932b2e4a5d 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -347,7 +347,6 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
static void snd_card_ymfpci_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
static struct pci_driver ymfpci_driver = {
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 22056c50fe39..d591c154fc58 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2258,7 +2258,7 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip)
/* FIXME: temporarily disabled, otherwise we cannot fire up
* the chip again unless reboot. ACPI bug?
*/
- pci_set_power_state(chip->pci, 3);
+ pci_set_power_state(chip->pci, PCI_D3hot);
#endif
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index 09fc848d32ec..8abb521b4814 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -139,7 +139,6 @@ __error:
static int snd_pmac_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index e59a73a9bc42..78a369785a9e 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -598,7 +598,6 @@ static int snd_aica_remove(struct platform_device *devptr)
return -ENODEV;
snd_card_free(dreamcastcard->card);
kfree(dreamcastcard);
- platform_set_drvdata(devptr, NULL);
return 0;
}
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index e68c4fc91a03..7c9422c4fc0f 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -290,8 +290,6 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
static int snd_sh_dac_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
-
return 0;
}
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 9e675c76436c..45eeaa9f7fec 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -51,6 +51,7 @@ source "sound/soc/pxa/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
+source "sound/soc/spear/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 197b6ae54c8d..bc0261476d7a 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
+obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 2d6fbd0125b9..802717eccbd0 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -38,8 +38,6 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
-#include <linux/pinctrl/consumer.h>
-
#include <linux/atmel-ssc.h>
#include <sound/core.h>
@@ -203,15 +201,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
struct device_node *codec_np, *cpu_np;
struct clk *pllb;
struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
- struct pinctrl *pinctrl;
int ret;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- dev_err(&pdev->dev, "Failed to request pinctrl for mck\n");
- return PTR_ERR(pinctrl);
- }
-
if (!np) {
if (!(machine_is_at91sam9g20ek() ||
machine_is_at91sam9g20ek_2mmc()))
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index 44b8dcecf571..d6f7694fcad4 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -179,13 +179,12 @@ static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
}
/* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops ac97c_bus_ops = {
.read = au1xac97c_ac97_read,
.write = au1xac97c_ac97_write,
.reset = au1xac97c_ac97_cold_reset,
.warm_reset = au1xac97c_ac97_warm_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops); /* globals be gone! */
static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
@@ -272,6 +271,10 @@ static int au1xac97c_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
+ ret = snd_soc_set_ac97_ops(&ac97c_bus_ops);
+ if (ret)
+ return ret;
+
ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component,
&au1xac97c_dai_driver, 1);
if (ret)
@@ -338,19 +341,7 @@ static struct platform_driver au1xac97c_driver = {
.remove = au1xac97c_drvremove,
};
-static int __init au1xac97c_load(void)
-{
- ac97c_workdata = NULL;
- return platform_driver_register(&au1xac97c_driver);
-}
-
-static void __exit au1xac97c_unload(void)
-{
- platform_driver_unregister(&au1xac97c_driver);
-}
-
-module_init(au1xac97c_load);
-module_exit(au1xac97c_unload);
+module_platform_driver(&au1xac97c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 8f1862aa7333..a822ab822bb7 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -201,13 +201,12 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
}
/* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops psc_ac97_ops = {
.read = au1xpsc_ac97_read,
.write = au1xpsc_ac97_write,
.reset = au1xpsc_ac97_cold_reset,
.warm_reset = au1xpsc_ac97_warm_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -383,15 +382,9 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
if (!iores)
return -ENODEV;
- if (!devm_request_mem_region(&pdev->dev, iores->start,
- resource_size(iores),
- pdev->name))
- return -EBUSY;
-
- wd->mmio = devm_ioremap(&pdev->dev, iores->start,
- resource_size(iores));
- if (!wd->mmio)
- return -EBUSY;
+ wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(wd->mmio))
+ return PTR_ERR(wd->mmio);
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmares)
@@ -423,6 +416,10 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
platform_set_drvdata(pdev, wd);
+ ret = snd_soc_set_ac97_ops(&psc_ac97_ops);
+ if (ret)
+ return ret;
+
ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
&wd->dai_drv, 1);
if (ret)
@@ -503,19 +500,7 @@ static struct platform_driver au1xpsc_ac97_driver = {
.remove = au1xpsc_ac97_drvremove,
};
-static int __init au1xpsc_ac97_load(void)
-{
- au1xpsc_ac97_workdata = NULL;
- return platform_driver_register(&au1xpsc_ac97_driver);
-}
-
-static void __exit au1xpsc_ac97_unload(void)
-{
- platform_driver_unregister(&au1xpsc_ac97_driver);
-}
-
-module_init(au1xpsc_ac97_load);
-module_exit(au1xpsc_ac97_unload);
+module_platform_driver(au1xpsc_ac97_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 16b88f5c26e2..54f74f8cbb75 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -56,6 +56,23 @@ config SND_SOC_BFIN_EVAL_ADAV80X
Note: This driver assumes that the ADAV80X digital record and playback
interfaces are connected to the first SPORT port on the BF5XX board.
+config SND_BF5XX_SOC_AD1836
+ tristate "SoC AD1836 Audio support for BF5xx"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_AD1836
+ help
+ Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+
+config SND_BF5XX_SOC_AD193X
+ tristate "SoC AD193X Audio support for Blackfin"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_AD193X
+ help
+ Say Y if you want to add support for AD193X codec on Blackfin.
+ This driver supports AD1936, AD1937, AD1938 and AD1939.
+
config SND_BF5XX_SOC_AD73311
tristate "SoC AD73311 Audio support for Blackfin"
depends on SND_BF5XX_I2S
@@ -72,33 +89,6 @@ config SND_BFIN_AD73311_SE
Enter the GPIO used to control AD73311's SE pin. Acceptable
values are 0 to 7
-config SND_BF5XX_TDM
- tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip"
- depends on (BLACKFIN && SND_SOC)
- select SND_BF5XX_SOC_SPORT
- help
- Say Y or M if you want to add support for codecs attached to
- the Blackfin SPORT (synchronous serial ports) interface in TDM
- mode.
- You will also need to select the audio interfaces to support below.
-
-config SND_BF5XX_SOC_AD1836
- tristate "SoC AD1836 Audio support for BF5xx"
- depends on SND_BF5XX_TDM
- select SND_BF5XX_SOC_TDM
- select SND_SOC_AD1836
- help
- Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
-
-config SND_BF5XX_SOC_AD193X
- tristate "SoC AD193X Audio support for Blackfin"
- depends on SND_BF5XX_TDM
- select SND_BF5XX_SOC_TDM
- select SND_SOC_AD193X
- help
- Say Y if you want to add support for AD193X codec on Blackfin.
- This driver supports AD1936, AD1937, AD1938 and AD1939.
-
config SND_BF5XX_AC97
tristate "SoC AC97 Audio for the ADI BF5xx chip"
depends on BLACKFIN
@@ -174,9 +164,6 @@ config SND_BF5XX_SOC_I2S
config SND_BF6XX_SOC_I2S
tristate
-config SND_BF5XX_SOC_TDM
- tristate
-
config SND_BF5XX_SOC_AC97
tristate
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 6fea1f4cbee2..ad0a6e99bc5d 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -1,23 +1,19 @@
# Blackfin Platform Support
snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
-snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
snd-soc-bf5xx-sport-objs := bf5xx-sport.o
snd-soc-bf6xx-sport-objs := bf6xx-sport.o
snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o
-snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
-obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o
obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o
-obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
# Blackfin Machine Support
snd-ad1836-objs := bf5xx-ad1836.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 7e2f36004a5a..53f84085bf1f 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -39,7 +39,6 @@
#include <asm/dma.h>
-#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
#include "bf5xx-sport.h"
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h
deleted file mode 100644
index d324d5826a9b..000000000000
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin
- *
- * Copyright 2007 Analog Device Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _BF5XX_AC97_PCM_H
-#define _BF5XX_AC97_PCM_H
-
-struct bf5xx_pcm_dma_params {
- char *name; /* stream identifier */
-};
-
-struct bf5xx_gpio {
- u32 sys;
- u32 rx;
- u32 tx;
- u32 clk;
- u32 frm;
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 490217325975..efb1daecd0dd 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -198,13 +198,12 @@ static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
#endif
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops bf5xx_ac97_ops = {
.read = bf5xx_ac97_read,
.write = bf5xx_ac97_write,
.warm_reset = bf5xx_ac97_warm_reset,
.reset = bf5xx_ac97_cold_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
#ifdef CONFIG_PM
static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
@@ -231,9 +230,9 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
return 0;
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport, 16, 0x3FF, 1);
+ ret = sport_set_multichannel(sport, 16, 0x3FF, 0x3FF, 1);
#else
- ret = sport_set_multichannel(sport, 16, 0x1F, 1);
+ ret = sport_set_multichannel(sport, 16, 0x1F, 0x1F, 1);
#endif
if (ret) {
pr_err("SPORT is busy!\n");
@@ -293,13 +292,14 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
- if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
- pr_err("Failed to request GPIO_%d for reset\n",
- CONFIG_SND_BF5XX_RESET_GPIO_NUM);
- ret = -1;
+ ret = devm_gpio_request_one(&pdev->dev,
+ CONFIG_SND_BF5XX_RESET_GPIO_NUM,
+ GPIOF_OUT_INIT_HIGH, "SND_AD198x RESET") {
+ dev_err(&pdev->dev,
+ "Failed to request GPIO_%d for reset: %d\n",
+ CONFIG_SND_BF5XX_RESET_GPIO_NUM, ret);
goto gpio_err;
}
- gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
@@ -311,9 +311,9 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
+ ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 0x3FF, 1);
#else
- ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+ ret = sport_set_multichannel(sport_handle, 16, 0x1F, 0x1F, 1);
#endif
if (ret) {
pr_err("SPORT is busy!\n");
@@ -335,6 +335,12 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
goto sport_config_err;
}
+ ret = snd_soc_set_ac97_ops(&bf5xx_ac97_ops);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+ goto sport_config_err;
+ }
+
ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component,
&bfin_ac97_dai, 1);
if (ret) {
@@ -349,10 +355,7 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
sport_config_err:
sport_done(sport_handle);
sport_err:
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-gpio_err:
-#endif
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
@@ -363,9 +366,7 @@ static int asoc_bfin_ac97_remove(struct platform_device *pdev)
snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index d23f4b0ea54f..8fcfc4ec3a51 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -30,15 +30,10 @@
#include "../codecs/ad1836.h"
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-
static struct snd_soc_card bf5xx_ad1836;
-static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
@@ -49,13 +44,13 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
+ if (ret < 0)
+ return ret;
+
return 0;
}
-static struct snd_soc_ops bf5xx_ad1836_ops = {
- .hw_params = bf5xx_ad1836_hw_params,
-};
-
#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
SND_SOC_DAIFMT_CBM_CFM)
@@ -63,9 +58,9 @@ static struct snd_soc_dai_link bf5xx_ad1836_dai = {
.name = "ad1836",
.stream_name = "AD1836",
.codec_dai_name = "ad1836-hifi",
- .platform_name = "bfin-tdm-pcm-audio",
- .ops = &bf5xx_ad1836_ops,
+ .platform_name = "bfin-i2s-pcm-audio",
.dai_fmt = BF5XX_AD1836_DAIFMT,
+ .init = bf5xx_ad1836_init,
};
static struct snd_soc_card bf5xx_ad1836 = {
diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c
index 0e55e9f2a514..603ad1f2b9b9 100644
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ b/sound/soc/blackfin/bf5xx-ad193x.c
@@ -39,30 +39,16 @@
#include "../codecs/ad193x.h"
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-
static struct snd_soc_card bf5xx_ad193x;
-static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int clk = 0;
- unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
- int ret = 0;
-
- switch (params_rate(params)) {
- case 48000:
- clk = 24576000;
- break;
- }
+ int ret;
/* set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
- SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -71,9 +57,7 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- /* set cpu DAI channel mapping */
- ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
- channel_map, ARRAY_SIZE(channel_map), channel_map);
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
return ret;
@@ -83,30 +67,26 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
#define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
SND_SOC_DAIFMT_CBM_CFM)
-static struct snd_soc_ops bf5xx_ad193x_ops = {
- .hw_params = bf5xx_ad193x_hw_params,
-};
-
static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
{
.name = "ad193x",
.stream_name = "AD193X",
- .cpu_dai_name = "bfin-tdm.0",
+ .cpu_dai_name = "bfin-i2s.0",
.codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-tdm-pcm-audio",
+ .platform_name = "bfin-i2s-pcm-audio",
.codec_name = "spi0.5",
- .ops = &bf5xx_ad193x_ops,
.dai_fmt = BF5XX_AD193X_DAIFMT,
+ .init = bf5xx_ad193x_link_init,
},
{
.name = "ad193x",
.stream_name = "AD193X",
- .cpu_dai_name = "bfin-tdm.1",
+ .cpu_dai_name = "bfin-i2s.1",
.codec_dai_name ="ad193x-hifi",
- .platform_name = "bfin-tdm-pcm-audio",
+ .platform_name = "bfin-i2s-pcm-audio",
.codec_name = "spi0.5",
- .ops = &bf5xx_ad193x_ops,
.dai_fmt = BF5XX_AD193X_DAIFMT,
+ .init = bf5xx_ad193x_link_init,
},
};
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index b30f88bbd703..3450e8f9080d 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -48,7 +48,6 @@
#include "../codecs/ad1980.h"
-#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index 61cc91d4a028..786bbdd96e7c 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -45,7 +45,6 @@
#include "../codecs/ad73311.h"
#include "bf5xx-sport.h"
-#include "bf5xx-i2s-pcm.h"
#if CONFIG_SND_BF5XX_SPORT_NUM == 0
#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 262c1de364d8..9cb4a80df98e 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -39,8 +39,8 @@
#include <asm/dma.h>
-#include "bf5xx-i2s-pcm.h"
#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
static void bf5xx_dma_irq(void *data)
{
@@ -50,7 +50,6 @@ static void bf5xx_dma_irq(void *data)
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
@@ -67,10 +66,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
- snd_pcm_lib_malloc_pages(substream, size);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int buffer_size = params_buffer_bytes(params);
+ struct bf5xx_i2s_pcm_data *dma_data;
- return 0;
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ if (dma_data->tdm_mode)
+ buffer_size = buffer_size / params_channels(params) * 8;
+
+ return snd_pcm_lib_malloc_pages(substream, buffer_size);
}
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -82,9 +87,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ struct bf5xx_i2s_pcm_data *dma_data;
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ if (dma_data->tdm_mode)
+ period_bytes = period_bytes / runtime->channels * 8;
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -131,10 +143,15 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
unsigned int diff;
snd_pcm_uframes_t frames;
+ struct bf5xx_i2s_pcm_data *dma_data;
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff = sport_curr_offset_tx(sport);
@@ -151,6 +168,8 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
diff = 0;
frames = bytes_to_frames(substream->runtime, diff);
+ if (dma_data->tdm_mode)
+ frames = frames * runtime->channels / 8;
return frames;
}
@@ -162,11 +181,18 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct bf5xx_i2s_pcm_data *dma_data;
int ret;
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
pr_debug("%s enter\n", __func__);
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+ if (dma_data->tdm_mode)
+ runtime->hw.buffer_bytes_max /= 4;
+ else
+ runtime->hw.info |= SNDRV_PCM_INFO_MMAP;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -202,6 +228,88 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
return 0 ;
}
+static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int sample_size = runtime->sample_bits / 8;
+ struct bf5xx_i2s_pcm_data *dma_data;
+ unsigned int i;
+ void *src, *dst;
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ if (dma_data->tdm_mode) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ src = buf;
+ dst = runtime->dma_area;
+ dst += pos * sample_size * 8;
+
+ while (count--) {
+ for (i = 0; i < runtime->channels; i++) {
+ memcpy(dst + dma_data->map[i] *
+ sample_size, src, sample_size);
+ src += sample_size;
+ }
+ dst += 8 * sample_size;
+ }
+ } else {
+ src = runtime->dma_area;
+ src += pos * sample_size * 8;
+ dst = buf;
+
+ while (count--) {
+ for (i = 0; i < runtime->channels; i++) {
+ memcpy(dst, src + dma_data->map[i] *
+ sample_size, sample_size);
+ dst += sample_size;
+ }
+ src += 8 * sample_size;
+ }
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ src = buf;
+ dst = runtime->dma_area;
+ dst += frames_to_bytes(runtime, pos);
+ } else {
+ src = runtime->dma_area;
+ src += frames_to_bytes(runtime, pos);
+ dst = buf;
+ }
+
+ memcpy(dst, src, frames_to_bytes(runtime, count));
+ }
+
+ return 0;
+}
+
+static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int sample_size = runtime->sample_bits / 8;
+ void *buf = runtime->dma_area;
+ struct bf5xx_i2s_pcm_data *dma_data;
+ unsigned int offset, size;
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ if (dma_data->tdm_mode) {
+ offset = pos * 8 * sample_size;
+ size = count * 8 * sample_size;
+ } else {
+ offset = frames_to_bytes(runtime, pos);
+ size = frames_to_bytes(runtime, count);
+ }
+
+ snd_pcm_format_set_silence(runtime->format, buf + offset, size);
+
+ return 0;
+}
+
static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
@@ -211,57 +319,16 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.trigger = bf5xx_pcm_trigger,
.pointer = bf5xx_pcm_pointer,
.mmap = bf5xx_pcm_mmap,
+ .copy = bf5xx_pcm_copy,
+ .silence = bf5xx_pcm_silence,
};
-static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area) {
- pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
- return -ENOMEM;
- }
- buf->bytes = size;
-
- pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
- buf->area, buf->bytes);
-
- return 0;
-}
-
-static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(NULL, buf->bytes, buf->area, 0);
- buf->area = NULL;
- }
-}
-
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
pr_debug("%s enter\n", __func__);
if (!card->dev->dma_mask)
@@ -269,27 +336,13 @@ static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
+ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV, card->dev, size, size);
}
static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
.ops = &bf5xx_pcm_i2s_ops,
.pcm_new = bf5xx_pcm_i2s_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
};
static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h
index 0c2c5a68d4ff..1f0435249f88 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.h
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h
@@ -1,26 +1,17 @@
/*
- * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin
- *
- * Copyright 2007 Analog Device Inc.
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#ifndef _BF5XX_I2S_PCM_H
-#define _BF5XX_I2S_PCM_H
+#ifndef _BF5XX_TDM_PCM_H
+#define _BF5XX_TDM_PCM_H
-struct bf5xx_pcm_dma_params {
- char *name; /* stream identifier */
-};
+#define BFIN_TDM_DAI_MAX_SLOTS 8
-struct bf5xx_gpio {
- u32 sys;
- u32 rx;
- u32 tx;
- u32 clk;
- u32 frm;
+struct bf5xx_i2s_pcm_data {
+ unsigned int map[BFIN_TDM_DAI_MAX_SLOTS];
+ bool tdm_mode;
};
#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index dd0c2a4f83a3..9a174fc47d39 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -42,6 +42,7 @@
#include <linux/gpio.h>
#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
struct bf5xx_i2s_port {
u16 tcr1;
@@ -49,6 +50,13 @@ struct bf5xx_i2s_port {
u16 tcr2;
u16 rcr2;
int configured;
+
+ unsigned int slots;
+ unsigned int tx_mask;
+ unsigned int rx_mask;
+
+ struct bf5xx_i2s_pcm_data tx_dma_data;
+ struct bf5xx_i2s_pcm_data rx_dma_data;
};
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -74,7 +82,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
- printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
+ dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n",
+ __func__);
ret = -EINVAL;
break;
}
@@ -88,7 +97,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
- printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
+ dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n",
+ __func__);
ret = -EINVAL;
break;
}
@@ -141,14 +151,14 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
}
@@ -162,18 +172,76 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- pr_debug("%s enter\n", __func__);
+ dev_dbg(dai->dev, "%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_i2s->configured = 0;
}
+static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+ unsigned int tx_mapped = 0, rx_mapped = 0;
+ unsigned int slot;
+ int i;
+
+ if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+ (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+ return -EINVAL;
+
+ for (i = 0; i < tx_num; i++) {
+ slot = tx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(tx_mapped & (1 << slot)))) {
+ bf5xx_i2s->tx_dma_data.map[i] = slot;
+ tx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++) {
+ slot = rx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(rx_mapped & (1 << slot)))) {
+ bf5xx_i2s->rx_dma_data.map[i] = slot;
+ rx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+
+ if (slots % 8 != 0 || slots > 8)
+ return -EINVAL;
+
+ if (width != 32)
+ return -EINVAL;
+
+ bf5xx_i2s->slots = slots;
+ bf5xx_i2s->tx_mask = tx_mask;
+ bf5xx_i2s->rx_mask = rx_mask;
+
+ bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
+ bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
+
+ return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
+}
+
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- pr_debug("%s : sport %d\n", __func__, dai->id);
+ dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
if (dai->capture_active)
sport_rx_stop(sport_handle);
@@ -188,23 +256,24 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
- pr_debug("%s : sport %d\n", __func__, dai->id);
+ dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
- return 0;
+ return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
+ bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
}
#else
@@ -212,6 +281,23 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
#define bf5xx_i2s_resume NULL
#endif
+static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+ unsigned int i;
+
+ for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
+ bf5xx_i2s->tx_dma_data.map[i] = i;
+ bf5xx_i2s->rx_dma_data.map[i] = i;
+ }
+
+ dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
+ dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
+
+ return 0;
+}
+
#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
@@ -224,22 +310,25 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,
- .set_fmt = bf5xx_i2s_set_dai_fmt,
+ .shutdown = bf5xx_i2s_shutdown,
+ .hw_params = bf5xx_i2s_hw_params,
+ .set_fmt = bf5xx_i2s_set_dai_fmt,
+ .set_tdm_slot = bf5xx_i2s_set_tdm_slot,
+ .set_channel_map = bf5xx_i2s_set_channel_map,
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
+ .probe = bf5xx_i2s_dai_probe,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
- .channels_min = 1,
- .channels_max = 2,
+ .channels_min = 2,
+ .channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.capture = {
- .channels_min = 1,
- .channels_max = 2,
+ .channels_min = 2,
+ .channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.ops = &bf5xx_i2s_dai_ops,
@@ -255,7 +344,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
int ret;
/* configure SPORT for I2S */
- sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
+ sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
sizeof(struct bf5xx_i2s_port));
if (!sport_handle)
return -ENODEV;
@@ -264,7 +353,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
&bf5xx_i2s_dai, 1);
if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
+ dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
return ret;
}
@@ -276,7 +365,7 @@ static int bf5xx_i2s_remove(struct platform_device *pdev)
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
- pr_debug("%s enter\n", __func__);
+ dev_dbg(&pdev->dev, "%s enter\n", __func__);
snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
index 2fd9f2a06968..695351241db8 100644
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -46,10 +46,10 @@
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
- int tdm_count, u32 mask, int packed)
+ int tdm_count, u32 tx_mask, u32 rx_mask, int packed)
{
- pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
- tdm_count, mask, packed);
+ pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n",
+ __func__, tdm_count, tx_mask, rx_mask, packed);
if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
return -EBUSY;
@@ -65,8 +65,8 @@ int sport_set_multichannel(struct sport_device *sport,
sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
(packed ? (MCDTXPE|MCDRXPE) : 0);
- sport->regs->mtcs0 = mask;
- sport->regs->mrcs0 = mask;
+ sport->regs->mtcs0 = tx_mask;
+ sport->regs->mrcs0 = rx_mask;
sport->regs->mtcs1 = 0;
sport->regs->mrcs1 = 0;
sport->regs->mtcs2 = 0;
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index 5ab60bd613ea..9fc2192feb3b 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -128,7 +128,7 @@ void sport_done(struct sport_device *sport);
/* note: multichannel is in units of 8 channels, tdm_count is number of channels
* NOT / 8 ! all channels are enabled by default */
int sport_set_multichannel(struct sport_device *sport, int tdm_count,
- u32 mask, int packed);
+ u32 tx_mask, u32 rx_mask, int packed);
int sport_config_rx(struct sport_device *sport,
unsigned int rcr1, unsigned int rcr2,
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index 7dbeef1099b4..9c19ccc936e2 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -40,7 +40,6 @@
#include <linux/gpio.h>
#include "../codecs/ssm2602.h"
#include "bf5xx-sport.h"
-#include "bf5xx-i2s-pcm.h"
static struct snd_soc_card bf5xx_ssm2602;
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
deleted file mode 100644
index 0e6b888bb4cc..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-tdm-pcm.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Tue June 06 2009
- * Description: DMA driver for tdm codec
- *
- * Modified:
- * Copyright 2009 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-
-#include "bf5xx-tdm-pcm.h"
-#include "bf5xx-tdm.h"
-#include "bf5xx-sport.h"
-
-#define PCM_BUFFER_MAX 0x8000
-#define FRAGMENT_SIZE_MIN (4*1024)
-#define FRAGMENTS_MIN 2
-#define FRAGMENTS_MAX 32
-
-static void bf5xx_dma_irq(void *data)
-{
- struct snd_pcm_substream *pcm = data;
- snd_pcm_period_elapsed(pcm);
-}
-
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .channels_min = 2,
- .channels_max = 8,
- .buffer_bytes_max = PCM_BUFFER_MAX,
- .period_bytes_min = FRAGMENT_SIZE_MIN,
- .period_bytes_max = PCM_BUFFER_MAX/2,
- .periods_min = FRAGMENTS_MIN,
- .periods_max = FRAGMENTS_MAX,
-};
-
-static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
- snd_pcm_lib_malloc_pages(substream, size * 4);
-
- return 0;
-}
-
-static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
-
- return 0;
-}
-
-static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
-
- fragsize_bytes /= runtime->channels;
- /* inflate the fragsize to match the dma width of SPORT */
- fragsize_bytes *= 8;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_tx_dma(sport, runtime->dma_area,
- runtime->periods, fragsize_bytes);
- } else {
- sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
- sport_config_rx_dma(sport, runtime->dma_area,
- runtime->periods, fragsize_bytes);
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_tx_start(sport);
- else
- sport_rx_start(sport);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_tx_stop(sport);
- else
- sport_rx_stop(sport);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- unsigned int diff;
- snd_pcm_uframes_t frames;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- diff = sport_curr_offset_tx(sport);
- frames = diff / (8*4); /* 32 bytes per frame */
- } else {
- diff = sport_curr_offset_rx(sport);
- frames = diff / (8*4);
- }
- return frames;
-}
-
-static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
-
- int ret = 0;
-
- snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- if (sport_handle != NULL) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
- runtime->private_data = sport_handle;
- } else {
- pr_err("sport_handle is NULL\n");
- ret = -ENODEV;
- }
-out:
- return ret;
-}
-
-static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
- struct bf5xx_tdm_port *tdm_port = sport->private_data;
- unsigned int *src;
- unsigned int *dst;
- int i;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = buf;
- dst = (unsigned int *)substream->runtime->dma_area;
-
- dst += pos * 8;
- while (count--) {
- for (i = 0; i < substream->runtime->channels; i++)
- *(dst + tdm_port->tx_map[i]) = *src++;
- dst += 8;
- }
- } else {
- src = (unsigned int *)substream->runtime->dma_area;
- dst = buf;
-
- src += pos * 8;
- while (count--) {
- for (i = 0; i < substream->runtime->channels; i++)
- *dst++ = *(src + tdm_port->rx_map[i]);
- src += 8;
- }
- }
-
- return 0;
-}
-
-static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
-{
- unsigned char *buf = substream->runtime->dma_area;
- buf += pos * 8 * 4;
- memset(buf, '\0', count * 8 * 4);
-
- return 0;
-}
-
-
-struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
- .open = bf5xx_pcm_open,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = bf5xx_pcm_hw_params,
- .hw_free = bf5xx_pcm_hw_free,
- .prepare = bf5xx_pcm_prepare,
- .trigger = bf5xx_pcm_trigger,
- .pointer = bf5xx_pcm_pointer,
- .copy = bf5xx_pcm_copy,
- .silence = bf5xx_pcm_silence,
-};
-
-static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
- &buf->addr, GFP_KERNEL);
- if (!buf->area) {
- pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
- return -ENOMEM;
- }
- buf->bytes = size;
-
- return 0;
-}
-
-static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(NULL, buf->bytes, buf->area, 0);
- buf->area = NULL;
- }
-}
-
-static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
-
-static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &bf5xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-out:
- return ret;
-}
-
-static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = {
- .ops = &bf5xx_pcm_tdm_ops,
- .pcm_new = bf5xx_pcm_tdm_new,
- .pcm_free = bf5xx_pcm_free_dma_buffers,
-};
-
-static int bf5xx_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform);
-}
-
-static int bf5xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver bfin_tdm_driver = {
- .driver = {
- .name = "bfin-tdm-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = bf5xx_soc_platform_probe,
- .remove = bf5xx_soc_platform_remove,
-};
-
-module_platform_driver(bfin_tdm_driver);
-
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h
deleted file mode 100644
index 7f8cc01c4477..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
- *
- * Copyright 2009 Analog Device Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _BF5XX_TDM_PCM_H
-#define _BF5XX_TDM_PCM_H
-
-struct bf5xx_pcm_dma_params {
- char *name; /* stream identifier */
-};
-
-#endif
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
deleted file mode 100644
index 69e9a3e935bd..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * File: sound/soc/blackfin/bf5xx-tdm.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: Thurs June 04 2009
- * Description: Blackfin I2S(TDM) CPU DAI driver
- * Even though TDM mode can be as part of I2S DAI, but there
- * are so much difference in configuration and data flow,
- * it's very ugly to integrate I2S and TDM into a module
- *
- * Modified:
- * Copyright 2009 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <asm/irq.h>
-#include <asm/portmux.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-
-#include "bf5xx-sport.h"
-#include "bf5xx-tdm.h"
-
-static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- int ret = 0;
-
- /* interface format:support TDM,slave mode */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- break;
- default:
- printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- ret = -EINVAL;
- break;
- default:
- printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
- int ret = 0;
-
- bf5xx_tdm->tcr2 &= ~0x1f;
- bf5xx_tdm->rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm->tcr2 |= 31;
- bf5xx_tdm->rcr2 |= 31;
- sport_handle->wdsize = 4;
- break;
- /* at present, we only support 32bit transfer */
- default:
- pr_err("not supported PCM format yet\n");
- return -EINVAL;
- break;
- }
-
- if (!bf5xx_tdm->configured) {
- /*
- * TX and RX are not independent,they are enabled at the
- * same time, even if only one side is running. So, we
- * need to configure both of them at the time when the first
- * stream is opened.
- *
- * CPU DAI:slave mode.
- */
- ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
- bf5xx_tdm->rcr2, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
- bf5xx_tdm->tcr2, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- return -EBUSY;
- }
-
- bf5xx_tdm->configured = 1;
- }
-
- return 0;
-}
-
-static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
-
- /* No active stream, SPORT is allowed to be configured again. */
- if (!dai->active)
- bf5xx_tdm->configured = 0;
-}
-
-static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
-{
- struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
- int i;
- unsigned int slot;
- unsigned int tx_mapped = 0, rx_mapped = 0;
-
- if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
- (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
- return -EINVAL;
-
- for (i = 0; i < tx_num; i++) {
- slot = tx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(tx_mapped & (1 << slot)))) {
- bf5xx_tdm->tx_map[i] = slot;
- tx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
- for (i = 0; i < rx_num; i++) {
- slot = rx_slot[i];
- if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
- (!(rx_mapped & (1 << slot)))) {
- bf5xx_tdm->rx_map[i] = slot;
- rx_mapped |= 1 << slot;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
-{
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- if (dai->playback_active)
- sport_tx_stop(sport);
- if (dai->capture_active)
- sport_rx_stop(sport);
-
- /* isolate sync/clock pins from codec while sports resume */
- peripheral_free_list(sport->pin_req);
-
- return 0;
-}
-
-static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
-{
- int ret;
- struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
-
- ret = sport_set_multichannel(sport, 8, 0xFF, 1);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- ret = sport_config_rx(sport, 0, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- ret = sport_config_tx(sport, 0, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- }
-
- peripheral_request_list(sport->pin_req, "soc-audio");
-
- return 0;
-}
-
-#else
-#define bf5xx_tdm_suspend NULL
-#define bf5xx_tdm_resume NULL
-#endif
-
-static const struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
- .hw_params = bf5xx_tdm_hw_params,
- .set_fmt = bf5xx_tdm_set_dai_fmt,
- .shutdown = bf5xx_tdm_shutdown,
- .set_channel_map = bf5xx_tdm_set_channel_map,
-};
-
-static struct snd_soc_dai_driver bf5xx_tdm_dai = {
- .suspend = bf5xx_tdm_suspend,
- .resume = bf5xx_tdm_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
- .ops = &bf5xx_tdm_dai_ops,
-};
-
-static const struct snd_soc_component_driver bf5xx_tdm_component = {
- .name = "bf5xx-tdm",
-};
-
-static int bfin_tdm_probe(struct platform_device *pdev)
-{
- struct sport_device *sport_handle;
- int ret;
-
- /* configure SPORT for TDM */
- sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
- sizeof(struct bf5xx_tdm_port));
- if (!sport_handle)
- return -ENODEV;
-
- /* SPORT works in TDM mode */
- ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0);
- if (ret) {
- pr_err("SPORT is busy!\n");
- ret = -EBUSY;
- goto sport_config_err;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &bf5xx_tdm_component,
- &bf5xx_tdm_dai, 1);
- if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
- goto sport_config_err;
- }
-
- return 0;
-
-sport_config_err:
- sport_done(sport_handle);
- return ret;
-}
-
-static int bfin_tdm_remove(struct platform_device *pdev)
-{
- struct sport_device *sport_handle = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
- sport_done(sport_handle);
-
- return 0;
-}
-
-static struct platform_driver bfin_tdm_driver = {
- .probe = bfin_tdm_probe,
- .remove = bfin_tdm_remove,
- .driver = {
- .name = "bfin-tdm",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(bfin_tdm_driver);
-
-/* Module information */
-MODULE_AUTHOR("Barry Song");
-MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
deleted file mode 100644
index e986a3ea3315..000000000000
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * sound/soc/blackfin/bf5xx-tdm.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _BF5XX_TDM_H
-#define _BF5XX_TDM_H
-
-#define BFIN_TDM_DAI_MAX_SLOTS 8
-struct bf5xx_tdm_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
- unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
- int configured;
-};
-
-#endif
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index 88143db7e753..2c20f01e1f7e 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -1,7 +1,7 @@
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX && SND_SOC
- select SND_SOC_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
the EP93xx I2S or AC97 interfaces.
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index 7798fbd5e81d..ac73c607410a 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -237,13 +237,12 @@ static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
.read = ep93xx_ac97_read,
.write = ep93xx_ac97_write,
.reset = ep93xx_ac97_cold_reset,
.warm_reset = ep93xx_ac97_warm_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
@@ -314,22 +313,15 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int ep93xx_ac97_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
{
- struct ep93xx_dma_data *dma_data;
+ dai->playback_dma_data = &ep93xx_ac97_pcm_out;
+ dai->capture_dma_data = &ep93xx_ac97_pcm_in;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = &ep93xx_ac97_pcm_out;
- else
- dma_data = &ep93xx_ac97_pcm_in;
-
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
return 0;
}
static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
- .startup = ep93xx_ac97_startup,
.trigger = ep93xx_ac97_trigger,
};
@@ -337,6 +329,7 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = {
.name = "ep93xx-ac97",
.id = 0,
.ac97_control = 1,
+ .probe = ep93xx_ac97_dai_probe,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -395,6 +388,10 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
ep93xx_ac97_info = info;
platform_set_drvdata(pdev, info);
+ ret = snd_soc_set_ac97_ops(&ep93xx_ac97_ops);
+ if (ret)
+ goto fail;
+
ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
&ep93xx_ac97_dai, 1);
if (ret)
@@ -403,9 +400,8 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
return 0;
fail:
- platform_set_drvdata(pdev, NULL);
ep93xx_ac97_info = NULL;
- dev_set_drvdata(&pdev->dev, NULL);
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
@@ -418,9 +414,9 @@ static int ep93xx_ac97_remove(struct platform_device *pdev)
/* disable the AC97 controller */
ep93xx_ac97_write_reg(info, AC97GCR, 0);
- platform_set_drvdata(pdev, NULL);
ep93xx_ac97_info = NULL;
- dev_set_drvdata(&pdev->dev, NULL);
+
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 5c1102e9e159..17ad70bca9fe 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -60,11 +60,10 @@ struct ep93xx_i2s_info {
struct clk *mclk;
struct clk *sclk;
struct clk *lrclk;
- struct ep93xx_dma_data *dma_data;
void __iomem *regs;
};
-struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
+static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = {
.name = "i2s-pcm-out",
.port = EP93XX_DMA_I2S1,
@@ -139,15 +138,11 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
}
}
-static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
- snd_soc_dai_set_dma_data(cpu_dai, substream,
- &info->dma_data[substream->stream]);
return 0;
}
@@ -338,7 +333,6 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
#endif
static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
- .startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
.set_sysclk = ep93xx_i2s_set_sysclk,
@@ -349,6 +343,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
.symmetric_rates= 1,
+ .probe = ep93xx_i2s_dai_probe,
.suspend = ep93xx_i2s_suspend,
.resume = ep93xx_i2s_resume,
.playback = {
@@ -407,7 +402,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
}
dev_set_drvdata(&pdev->dev, info);
- info->dma_data = ep93xx_i2s_dma_data;
err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
&ep93xx_i2s_dai, 1);
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 488032690378..0e9f56e0d4b2 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -14,20 +14,14 @@
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
+#include <linux/platform_device.h>
#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <sound/core.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <linux/platform_data/dma-ep93xx.h>
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
@@ -63,134 +57,24 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
return false;
}
-static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
-
- return snd_dmaengine_pcm_open_request_chan(substream,
- ep93xx_pcm_dma_filter,
- snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
-}
-
-static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
- return 0;
-}
-
-static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
-static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops ep93xx_pcm_ops = {
- .open = ep93xx_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = ep93xx_pcm_hw_params,
- .hw_free = ep93xx_pcm_hw_free,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer_no_residue,
- .mmap = ep93xx_pcm_mmap,
-};
-
-static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- buf->bytes = size;
-
- return (buf->area == NULL) ? -ENOMEM : 0;
-}
-
-static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
- buf->addr);
- buf->area = NULL;
- }
-}
-
-static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
-
-static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &ep93xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver ep93xx_soc_platform = {
- .ops = &ep93xx_pcm_ops,
- .pcm_new = &ep93xx_pcm_new,
- .pcm_free = &ep93xx_pcm_free_dma_buffers,
+static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = {
+ .pcm_hardware = &ep93xx_pcm_hardware,
+ .compat_filter_fn = ep93xx_pcm_dma_filter,
+ .prealloc_buffer_size = 131072,
};
static int ep93xx_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
+ return snd_dmaengine_pcm_register(&pdev->dev,
+ &ep93xx_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
+ SND_DMAENGINE_PCM_FLAG_NO_DT |
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static int ep93xx_soc_platform_remove(struct platform_device *pdev)
{
- snd_soc_unregister_platform(&pdev->dev);
+ snd_dmaengine_pcm_unregister(&pdev->dev);
return 0;
}
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 60159c07448d..8af04343cc1a 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,10 +120,8 @@
* before DAC & PGA in DAPM power-off sequence.
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrol_news = NULL, \
- .num_kcontrols = 0, .event = wevent, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
struct pm860x_det {
struct snd_soc_jack *hp_jack;
@@ -1444,7 +1442,7 @@ static int pm860x_codec_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Failed to get IRQ resources\n");
- goto out;
+ return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
@@ -1454,19 +1452,14 @@ static int pm860x_codec_probe(struct platform_device *pdev)
pm860x_dai, ARRAY_SIZE(pm860x_dai));
if (ret) {
dev_err(&pdev->dev, "Failed to register codec\n");
- goto out;
+ return -EINVAL;
}
return ret;
-
-out:
- platform_set_drvdata(pdev, NULL);
- return -EINVAL;
}
static int pm860x_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 2f45f00e31b0..badb6fbacaa6 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -19,7 +19,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
- select SND_SOC_ADAV80X
+ select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -40,7 +40,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA7213 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
- select SND_SOC_DFBMCS320
+ select SND_SOC_BT_SCO
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@@ -53,13 +53,15 @@ 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_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI
+ select SND_SOC_HDMI_CODEC
select SND_SOC_PCM3008
select SND_SOC_RT5631 if I2C
+ select SND_SOC_RT5640 if I2C
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
+ select SND_SOC_SSM2518 if I2C
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STA32X if I2C
select SND_SOC_STA529 if I2C
@@ -263,7 +265,7 @@ config SND_SOC_DA732X
config SND_SOC_DA9055
tristate
-config SND_SOC_DFBMCS320
+config SND_SOC_BT_SCO
tristate
config SND_SOC_DMIC
@@ -287,7 +289,7 @@ config SND_SOC_MAX98095
config SND_SOC_MAX9850
tristate
-config SND_SOC_OMAP_HDMI_CODEC
+config SND_SOC_HDMI_CODEC
tristate
config SND_SOC_PCM3008
@@ -296,6 +298,9 @@ config SND_SOC_PCM3008
config SND_SOC_RT5631
tristate
+config SND_SOC_RT5640
+ tristate
+
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate
@@ -313,6 +318,9 @@ config SND_SOC_SN95031
config SND_SOC_SPDIF
tristate
+config SND_SOC_SSM2518
+ tristate
+
config SND_SOC_SSM2602
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b9e41c9a1f4c..70fd8066f546 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -27,7 +27,7 @@ snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
-snd-soc-dfbmcs320-objs := dfbmcs320.o
+snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
@@ -41,17 +41,19 @@ snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
-snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
+snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-rt5631-objs := rt5631.o
+snd-soc-rt5640-objs := rt5640.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-si476x-objs := si476x.o
snd-soc-sn95031-objs := sn95031.o
-snd-soc-spdif-tx-objs := spdif_transciever.o
+snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
+snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta529-objs := sta529.o
@@ -154,7 +156,7 @@ obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
-obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
+obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
@@ -168,14 +170,16 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.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_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
+obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index a153b168129b..b8ba0adacfce 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1496,6 +1496,12 @@ static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
"AD_OUT7",
"AD_OUT8",
"zeroes",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "tristate",
+ "tristate",
+ "tristate",
"tristate"};
static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
@@ -2230,7 +2236,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
- unsigned int val, mask, slots_active;
+ unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
BIT(AB8500_DIGIFCONF2_IF0WL1);
@@ -2286,27 +2292,34 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
+
+ if (tx_mask & ~0xff)
+ return -EINVAL;
+
mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+ tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
+
dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
+
switch (slots_active) {
case 0:
break;
case 1:
- /* Slot 9 -> DA_IN1 & DA_IN3 */
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
+ slot = find_first_bit((unsigned long *)&tx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 2:
- /* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
-
+ slot = find_first_bit((unsigned long *)&tx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ slot = find_next_bit((unsigned long *)&tx_mask, 32, slot + 1);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 8:
dev_dbg(dai->codec->dev,
@@ -2321,25 +2334,36 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
}
/* Setup TDM AD according to active RX-slots */
+
+ if (rx_mask & ~0xff)
+ return -EINVAL;
+
+ rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
+
dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
+
switch (slots_active) {
case 0:
break;
case 1:
- /* AD_OUT3 -> slot 0 & 1 */
- snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL,
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD);
+ slot = find_first_bit((unsigned long *)&rx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
break;
case 2:
- /* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */
+ slot = find_first_bit((unsigned long *)&rx_mask, 32);
snd_soc_update_bits(codec,
- AB8500_ADSLOTSEL1,
- AB8500_MASK_ALL,
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
- AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD);
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
+ slot = find_next_bit((unsigned long *)&rx_mask, 32, slot + 1);
+ snd_soc_update_bits(codec,
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
break;
case 8:
dev_dbg(dai->codec->dev,
@@ -2356,6 +2380,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static const struct snd_soc_dai_ops ab8500_codec_ops = {
+ .set_fmt = ab8500_codec_set_dai_fmt,
+ .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+};
+
static struct snd_soc_dai_driver ab8500_codec_dai[] = {
{
.name = "ab8500-codec-dai.0",
@@ -2367,12 +2396,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
- .ops = (struct snd_soc_dai_ops[]) {
- {
- .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
- .set_fmt = ab8500_codec_set_dai_fmt,
- }
- },
+ .ops = &ab8500_codec_ops,
.symmetric_rates = 1
},
{
@@ -2385,12 +2409,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
- .ops = (struct snd_soc_dai_ops[]) {
- {
- .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
- .set_fmt = ab8500_codec_set_dai_fmt,
- }
- },
+ .ops = &ab8500_codec_ops,
.symmetric_rates = 1
}
};
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
index 306d0bc8455f..e2e54425d25e 100644
--- a/sound/soc/codecs/ab8500-codec.h
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -24,6 +24,13 @@
#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
+/* AB8500 interface slot offset definitions */
+
+#define AB8500_AD_DATA0_OFFSET 0
+#define AB8500_DA_DATA0_OFFSET 8
+#define AB8500_AD_DATA1_OFFSET 16
+#define AB8500_DA_DATA1_OFFSET 24
+
/* AB8500 audio bank (0x0d) register definitions */
#define AB8500_POWERUP 0x00
@@ -73,6 +80,7 @@
#define AB8500_ADSLOTSEL14 0x2C
#define AB8500_ADSLOTSEL15 0x2D
#define AB8500_ADSLOTSEL16 0x2E
+#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
#define AB8500_ADSLOTHIZCTRL1 0x2F
#define AB8500_ADSLOTHIZCTRL2 0x30
#define AB8500_ADSLOTHIZCTRL3 0x31
@@ -144,6 +152,7 @@
#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
#define AB8500_MASK_ALL 0xFF
+#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
#define AB8500_MASK_NONE 0x00
/* AB8500_POWERUP */
@@ -347,28 +356,21 @@
#define AB8500_DIGIFCONF4_IF1WL0 0
/* AB8500_ADSLOTSELX */
-#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD 0x00
-#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD 0x10
-#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD 0x20
-#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD 0x30
-#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD 0x40
-#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD 0x50
-#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD 0x60
-#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD 0x70
-#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD 0x80
-#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD 0xF0
-#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN 0x00
-#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN 0x01
-#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN 0x02
-#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN 0x03
-#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN 0x04
-#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN 0x05
-#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN 0x06
-#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN 0x07
-#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN 0x08
-#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN 0x0F
+#define AB8500_AD_OUT1 0x0
+#define AB8500_AD_OUT2 0x1
+#define AB8500_AD_OUT3 0x2
+#define AB8500_AD_OUT4 0x3
+#define AB8500_AD_OUT5 0x4
+#define AB8500_AD_OUT6 0x5
+#define AB8500_AD_OUT7 0x6
+#define AB8500_AD_OUT8 0x7
+#define AB8500_ZEROES 0x8
+#define AB8500_TRISTATE 0xF
#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
#define AB8500_ADSLOTSELX_ODD_SHIFT 4
+#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
+ ((out) << (((slot) & 1) ? \
+ AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
/* AB8500_ADSLOTHIZCTRL1 */
/* AB8500_ADSLOTHIZCTRL2 */
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index ef2ae32ffc66..ec7351803c24 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -62,13 +62,13 @@ static struct snd_soc_dai_driver ac97_dai = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
return 0;
}
@@ -79,7 +79,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+ ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
+ &ac97_bus);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index f385342947d3..89fcf7d6e7b8 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -108,7 +108,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
case AC97_EXTENDED_STATUS:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -124,7 +124,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
@@ -154,13 +154,13 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
u16 retry_cnt = 0;
retry:
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, AC97_RESET) == 0x0090)
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
/* Set bit 16slot in register 74h, then every slot will has only 16
* bits. This command is sent out in 20bit mode, in which case the
* first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
@@ -186,7 +186,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index dafdbe87edeb..d1124a5b3471 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -13,6 +13,10 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -21,16 +25,19 @@
#include "sigmadsp.h"
#include "adau1701.h"
-#define ADAU1701_DSPCTRL 0x1c
-#define ADAU1701_SEROCTL 0x1e
-#define ADAU1701_SERICTL 0x1f
+#define ADAU1701_DSPCTRL 0x081c
+#define ADAU1701_SEROCTL 0x081e
+#define ADAU1701_SERICTL 0x081f
-#define ADAU1701_AUXNPOW 0x22
+#define ADAU1701_AUXNPOW 0x0822
+#define ADAU1701_PINCONF_0 0x0820
+#define ADAU1701_PINCONF_1 0x0821
+#define ADAU1701_AUXNPOW 0x0822
-#define ADAU1701_OSCIPOW 0x26
-#define ADAU1701_DACSET 0x27
+#define ADAU1701_OSCIPOW 0x0826
+#define ADAU1701_DACSET 0x0827
-#define ADAU1701_NUM_REGS 0x28
+#define ADAU1701_MAX_REGISTER 0x0828
#define ADAU1701_DSPCTRL_CR (1 << 2)
#define ADAU1701_DSPCTRL_DAM (1 << 3)
@@ -84,10 +91,18 @@
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
+#define ADAU1707_CLKDIV_UNSET (-1UL)
+
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
+ int gpio_nreset;
+ int gpio_pll_mode[2];
unsigned int dai_fmt;
+ unsigned int pll_clkdiv;
+ unsigned int sysclk;
+ struct regmap *regmap;
+ u8 pin_config[12];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -119,10 +134,13 @@ static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
{ "ADC", NULL, "IN1" },
};
-static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
+static unsigned int adau1701_register_size(struct device *dev,
unsigned int reg)
{
switch (reg) {
+ case ADAU1701_PINCONF_0:
+ case ADAU1701_PINCONF_1:
+ return 3;
case ADAU1701_DSPCTRL:
case ADAU1701_SEROCTL:
case ADAU1701_AUXNPOW:
@@ -133,33 +151,42 @@ static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
return 1;
}
- dev_err(codec->dev, "Unsupported register address: %d\n", reg);
+ dev_err(dev, "Unsupported register address: %d\n", reg);
return 0;
}
-static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DACSET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+ unsigned int value)
{
+ struct i2c_client *client = context;
unsigned int i;
unsigned int size;
- uint8_t buf[4];
+ uint8_t buf[5];
int ret;
- size = adau1701_register_size(codec, reg);
+ size = adau1701_register_size(&client->dev, reg);
if (size == 0)
return -EINVAL;
- snd_soc_cache_write(codec, reg, value);
-
- buf[0] = 0x08;
- buf[1] = reg;
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
for (i = size + 1; i >= 2; --i) {
buf[i] = value;
value >>= 8;
}
- ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
+ ret = i2c_master_send(client, buf, size + 2);
if (ret == size + 2)
return 0;
else if (ret < 0)
@@ -168,21 +195,107 @@ static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
+static int adau1701_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
{
- unsigned int value;
- unsigned int ret;
+ int ret;
+ unsigned int i;
+ unsigned int size;
+ uint8_t send_buf[2], recv_buf[3];
+ struct i2c_client *client = context;
+ struct i2c_msg msgs[2];
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
- ret = snd_soc_cache_read(codec, reg, &value);
- if (ret)
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
- return value;
+ *value = 0;
+
+ for (i = 0; i < size; i++)
+ *value |= recv_buf[i] << (i * 8);
+
+ return 0;
}
-static int adau1701_load_firmware(struct snd_soc_codec *codec)
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
{
- return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+ int ret;
+
+ if (clkdiv != ADAU1707_CLKDIV_UNSET &&
+ gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
+ gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ switch (clkdiv) {
+ case 64:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 256:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ break;
+ case 384:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 0: /* fallback */
+ case 512:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ break;
+ }
+ }
+
+ adau1701->pll_clkdiv = clkdiv;
+
+ if (gpio_is_valid(adau1701->gpio_nreset)) {
+ gpio_set_value(adau1701->gpio_nreset, 0);
+ /* minimum reset time is 20ns */
+ udelay(1);
+ gpio_set_value(adau1701->gpio_nreset, 1);
+ /* power-up time may be as long as 85ms */
+ mdelay(85);
+ }
+
+ /*
+ * Postpone the firmware download to a point in time when we
+ * know the correct PLL setup
+ */
+ if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+ ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+ if (ret) {
+ dev_warn(codec->dev, "Failed to load firmware\n");
+ return ret;
+ }
+ }
+
+ regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ regcache_mark_dirty(adau1701->regmap);
+ regcache_sync(adau1701->regmap);
+
+ return 0;
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
@@ -221,7 +334,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
}
- snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
return 0;
}
@@ -249,7 +362,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_SERICTL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
ADAU1701_SERICTL_MODE_MASK, val);
return 0;
@@ -259,8 +372,22 @@ static int adau1701_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 adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int clkdiv = adau1701->sysclk / params_rate(params);
snd_pcm_format_t format;
unsigned int val;
+ int ret;
+
+ /*
+ * If the mclk/lrclk ratio changes, the chip needs updated PLL
+ * mode GPIO settings, and a full reset cycle, including a new
+ * firmware upload.
+ */
+ if (clkdiv != adau1701->pll_clkdiv) {
+ ret = adau1701_reset(codec, clkdiv);
+ if (ret < 0)
+ return ret;
+ }
switch (params_rate(params)) {
case 192000:
@@ -276,7 +403,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
ADAU1701_DSPCTRL_SR_MASK, val);
format = params_format(params);
@@ -352,8 +479,8 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- snd_soc_write(codec, ADAU1701_SERICTL, serictl);
- snd_soc_update_bits(codec, ADAU1701_SEROCTL,
+ regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
return 0;
@@ -363,6 +490,7 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -371,11 +499,13 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* Enable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, 0x00);
break;
case SND_SOC_BIAS_OFF:
/* Disable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, mask);
break;
}
@@ -387,6 +517,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (mute)
@@ -394,7 +525,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
else
val = mask;
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
return 0;
}
@@ -403,6 +534,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -415,7 +547,9 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+ ADAU1701_OSCIPOW_OPD, val);
+ adau1701->sysclk = freq;
return 0;
}
@@ -452,18 +586,45 @@ static struct snd_soc_dai_driver adau1701_dai = {
.symmetric_rates = 1,
};
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+ { .compatible = "adi,adau1701", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
static int adau1701_probe(struct snd_soc_codec *codec)
{
- int ret;
+ int i, ret;
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Let the pll_clkdiv variable default to something that won't happen
+ * at runtime. That way, we can postpone the firmware download from
+ * adau1701_reset() to a point in time when we know the correct PLL
+ * mode parameters.
+ */
+ adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
+
+ /* initalize with pre-configured pll mode settings */
+ ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+ if (ret < 0)
+ return ret;
+
+ /* set up pin config */
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i] << (i * 4);
- codec->control_data = to_i2c_client(codec->dev);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
- ret = adau1701_load_firmware(codec);
- if (ret)
- dev_warn(codec->dev, "Failed to load firmware\n");
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i + 6] << (i * 4);
- snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
- snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
return 0;
}
@@ -473,9 +634,6 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.set_bias_level = adau1701_set_bias_level,
.idle_bias_off = true,
- .reg_cache_size = ADAU1701_NUM_REGS,
- .reg_word_size = sizeof(u16),
-
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
@@ -483,22 +641,86 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
- .write = adau1701_write,
- .read = adau1701_read,
-
.set_sysclk = adau1701_set_sysclk,
};
+static const struct regmap_config adau1701_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = ADAU1701_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = adau1701_volatile_reg,
+ .reg_write = adau1701_reg_write,
+ .reg_read = adau1701_reg_read,
+};
+
static int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
+ struct device *dev = &client->dev;
+ int gpio_nreset = -EINVAL;
+ int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret;
- adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL);
+ adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
+ adau1701->regmap = devm_regmap_init(dev, NULL, client,
+ &adau1701_regmap);
+ if (IS_ERR(adau1701->regmap))
+ return PTR_ERR(adau1701->regmap);
+
+ if (dev->of_node) {
+ gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+ if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
+ return gpio_nreset;
+
+ gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 0);
+ if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
+ return gpio_pll_mode[0];
+
+ gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 1);
+ if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
+ return gpio_pll_mode[1];
+
+ of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+ &adau1701->pll_clkdiv);
+
+ of_property_read_u8_array(dev->of_node, "adi,pin-config",
+ adau1701->pin_config,
+ ARRAY_SIZE(adau1701->pin_config));
+ }
+
+ if (gpio_is_valid(gpio_nreset)) {
+ ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
+ "ADAU1701 Reset");
+ if (ret < 0)
+ return ret;
+ }
+
+ if (gpio_is_valid(gpio_pll_mode[0]) &&
+ gpio_is_valid(gpio_pll_mode[1])) {
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 0");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 1");
+ if (ret < 0)
+ return ret;
+ }
+
+ adau1701->gpio_nreset = gpio_nreset;
+ adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
+ adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
+
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
@@ -521,6 +743,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
.remove = adau1701_i2c_remove,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 389f23253831..de625813c0e6 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1198,6 +1198,13 @@ const struct snd_soc_dai_ops arizona_dai_ops = {
};
EXPORT_SYMBOL_GPL(arizona_dai_ops);
+const struct snd_soc_dai_ops arizona_simple_dai_ops = {
+ .startup = arizona_startup,
+ .hw_params = arizona_hw_params_rate,
+ .set_sysclk = arizona_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(arizona_simple_dai_ops);
+
int arizona_init_dai(struct arizona_priv *priv, int id)
{
struct arizona_dai_priv *dai_priv = &priv->dai[id];
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index af39f1006427..b60b08ccc1d0 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 4
+#define ARIZONA_MAX_DAI 6
#define ARIZONA_MAX_ADSP 4
struct arizona;
@@ -213,6 +213,7 @@ extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
+extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
#define ARIZONA_FLL_NAME_LEN 20
diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/bt-sco.c
index 4f4f7f41a7d1..a081d9fcb166 100644
--- a/sound/soc/codecs/dfbmcs320.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -1,5 +1,5 @@
/*
- * Driver for the DFBM-CS320 bluetooth module
+ * Driver for generic Bluetooth SCO link
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify it
@@ -15,8 +15,8 @@
#include <sound/soc.h>
-static struct snd_soc_dai_driver dfbmcs320_dai = {
- .name = "dfbmcs320-pcm",
+static struct snd_soc_dai_driver bt_sco_dai = {
+ .name = "bt-sco-pcm",
.playback = {
.channels_min = 1,
.channels_max = 1,
@@ -31,32 +31,41 @@ static struct snd_soc_dai_driver dfbmcs320_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
+static struct snd_soc_codec_driver soc_codec_dev_bt_sco;
-static int dfbmcs320_probe(struct platform_device *pdev)
+static int bt_sco_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
- &dfbmcs320_dai, 1);
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
+ &bt_sco_dai, 1);
}
-static int dfbmcs320_remove(struct platform_device *pdev)
+static int bt_sco_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
-static struct platform_driver dfmcs320_driver = {
+static struct platform_device_id bt_sco_driver_ids[] = {
+ {
+ .name = "dfbmcs320",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
+
+static struct platform_driver bt_sco_driver = {
.driver = {
- .name = "dfbmcs320",
+ .name = "bt-sco",
.owner = THIS_MODULE,
},
- .probe = dfbmcs320_probe,
- .remove = dfbmcs320_remove,
+ .probe = bt_sco_probe,
+ .remove = bt_sco_remove,
+ .id_table = bt_sco_driver_ids,
};
-module_platform_driver(dfmcs320_driver);
+module_platform_driver(bt_sco_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
+MODULE_DESCRIPTION("ASoC generic bluethooth sco link driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/hdmi.c
index 529d06444c54..2bcae2b40c92 100644
--- a/sound/soc/codecs/omap-hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -1,5 +1,5 @@
/*
- * ALSA SoC codec driver for HDMI audio on OMAP processors.
+ * 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>
*
@@ -23,10 +23,10 @@
#define DRV_NAME "hdmi-audio-codec"
-static struct snd_soc_codec_driver omap_hdmi_codec;
+static struct snd_soc_codec_driver hdmi_codec;
-static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
- .name = "omap-hdmi-hifi",
+static struct snd_soc_dai_driver hdmi_codec_dai = {
+ .name = "hdmi-hifi",
.playback = {
.channels_min = 2,
.channels_max = 8,
@@ -39,31 +39,31 @@ static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
},
};
-static int omap_hdmi_codec_probe(struct platform_device *pdev)
+static int hdmi_codec_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec,
- &omap_hdmi_codec_dai, 1);
+ return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
+ &hdmi_codec_dai, 1);
}
-static int omap_hdmi_codec_remove(struct platform_device *pdev)
+static int hdmi_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
-static struct platform_driver omap_hdmi_codec_driver = {
+static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .probe = omap_hdmi_codec_probe,
- .remove = omap_hdmi_codec_remove,
+ .probe = hdmi_codec_probe,
+ .remove = hdmi_codec_remove,
};
-module_platform_driver(omap_hdmi_codec_driver);
+module_platform_driver(hdmi_codec_driver);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver");
+MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 5f607b35b68b..bcebd1a9ce31 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -384,8 +384,6 @@ static int jz4740_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 8d14a76c7249..ad5313f98f28 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -857,6 +857,14 @@ static const struct soc_enum mic2_mux_enum =
static const struct snd_kcontrol_new max98090_mic2_mux =
SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
+static const char *dmic_mux_text[] = { "ADC", "DMIC" };
+
+static const struct soc_enum dmic_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text);
+
+static const struct snd_kcontrol_new max98090_dmic_mux =
+ SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum);
+
static const char *max98090_micpre_text[] = { "Off", "On" };
static const struct soc_enum max98090_pa1en_enum =
@@ -1144,6 +1152,9 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
0, 0, &max98090_mic2_mux),
+ SND_SOC_DAPM_VIRT_MUX("DMIC Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_dmic_mux),
+
SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -1336,11 +1347,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"ADCL", NULL, "SHDN"},
{"ADCR", NULL, "SHDN"},
- {"LBENL Mux", "Normal", "ADCL"},
- {"LBENL Mux", "Normal", "DMICL"},
+ {"DMIC Mux", "ADC", "ADCL"},
+ {"DMIC Mux", "ADC", "ADCR"},
+ {"DMIC Mux", "DMIC", "DMICL"},
+ {"DMIC Mux", "DMIC", "DMICR"},
+
+ {"LBENL Mux", "Normal", "DMIC Mux"},
{"LBENL Mux", "Loopback", "LTENL Mux"},
- {"LBENR Mux", "Normal", "ADCR"},
- {"LBENR Mux", "Normal", "DMICR"},
+ {"LBENR Mux", "Normal", "DMIC Mux"},
{"LBENR Mux", "Loopback", "LTENR Mux"},
{"AIFOUTL", NULL, "LBENL Mux"},
@@ -2336,6 +2350,7 @@ static int max98090_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
static int max98090_runtime_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2355,6 +2370,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
+#endif
static const struct dev_pm_ops max98090_pm = {
SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
new file mode 100644
index 000000000000..ce585e37e38a
--- /dev/null
+++ b/sound/soc/codecs/rt5640.c
@@ -0,0 +1,2128 @@
+/*
+ * rt5640.c -- RT5640 ALSA SoC audio codec driver
+ *
+ * Copyright 2011 Realtek Semiconductor Corp.
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ * Copyright (c) 2013, NVIDIA 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.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 "rt5640.h"
+
+#define RT5640_DEVICE_ID 0x6231
+
+#define RT5640_PR_RANGE_BASE (0xff + 1)
+#define RT5640_PR_SPACING 0x100
+
+#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING))
+
+static const struct regmap_range_cfg rt5640_ranges[] = {
+ { .name = "PR", .range_min = RT5640_PR_BASE,
+ .range_max = RT5640_PR_BASE + 0xb4,
+ .selector_reg = RT5640_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5640_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static struct reg_default init_list[] = {
+ {RT5640_PR_BASE + 0x3d, 0x3600},
+ {RT5640_PR_BASE + 0x1c, 0x0D21},
+ {RT5640_PR_BASE + 0x1b, 0x0000},
+ {RT5640_PR_BASE + 0x12, 0x0aa8},
+ {RT5640_PR_BASE + 0x14, 0x0aaa},
+ {RT5640_PR_BASE + 0x20, 0x6110},
+ {RT5640_PR_BASE + 0x21, 0xe0e0},
+ {RT5640_PR_BASE + 0x23, 0x1804},
+};
+#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5640_reg[RT5640_VENDOR_ID2 + 1] = {
+ { 0x00, 0x000e },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x04, 0x8000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5454 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaa00 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0xa000 },
+ { 0x2f, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf800 },
+ { 0x49, 0x3800 },
+ { 0x4a, 0x0004 },
+ { 0x4c, 0xfc00 },
+ { 0x4d, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x1114 },
+ { 0x74, 0x0c00 },
+ { 0x75, 0x1d00 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0008 },
+ { 0x89, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8b, 0x0600 },
+ { 0x8c, 0x0228 },
+ { 0x8d, 0xa000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c00 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x3000 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb8, 0x034b },
+ { 0xb9, 0x0066 },
+ { 0xba, 0x000b },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0400 },
+ { 0xc2, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x2000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0000 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd2, 0x8c00 },
+ { 0xd3, 0xaa20 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6231 },
+};
+
+static int rt5640_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5640_RESET, 0);
+}
+
+static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_ASRC_5:
+ case RT5640_EQ_CTRL1:
+ case RT5640_DRC_AGC_1:
+ case RT5640_ANC_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5640_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_SPK_VOL:
+ case RT5640_HP_VOL:
+ case RT5640_OUTPUT:
+ case RT5640_MONO_OUT:
+ case RT5640_IN1_IN2:
+ case RT5640_IN3_IN4:
+ case RT5640_INL_INR_VOL:
+ case RT5640_DAC1_DIG_VOL:
+ case RT5640_DAC2_DIG_VOL:
+ case RT5640_DAC2_CTRL:
+ case RT5640_ADC_DIG_VOL:
+ case RT5640_ADC_DATA:
+ case RT5640_ADC_BST_VOL:
+ case RT5640_STO_ADC_MIXER:
+ case RT5640_MONO_ADC_MIXER:
+ case RT5640_AD_DA_MIXER:
+ case RT5640_STO_DAC_MIXER:
+ case RT5640_MONO_DAC_MIXER:
+ case RT5640_DIG_MIXER:
+ case RT5640_DSP_PATH1:
+ case RT5640_DSP_PATH2:
+ case RT5640_DIG_INF_DATA:
+ case RT5640_REC_L1_MIXER:
+ case RT5640_REC_L2_MIXER:
+ case RT5640_REC_R1_MIXER:
+ case RT5640_REC_R2_MIXER:
+ case RT5640_HPO_MIXER:
+ case RT5640_SPK_L_MIXER:
+ case RT5640_SPK_R_MIXER:
+ case RT5640_SPO_L_MIXER:
+ case RT5640_SPO_R_MIXER:
+ case RT5640_SPO_CLSD_RATIO:
+ case RT5640_MONO_MIXER:
+ case RT5640_OUT_L1_MIXER:
+ case RT5640_OUT_L2_MIXER:
+ case RT5640_OUT_L3_MIXER:
+ case RT5640_OUT_R1_MIXER:
+ case RT5640_OUT_R2_MIXER:
+ case RT5640_OUT_R3_MIXER:
+ case RT5640_LOUT_MIXER:
+ case RT5640_PWR_DIG1:
+ case RT5640_PWR_DIG2:
+ case RT5640_PWR_ANLG1:
+ case RT5640_PWR_ANLG2:
+ case RT5640_PWR_MIXER:
+ case RT5640_PWR_VOL:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_I2S1_SDP:
+ case RT5640_I2S2_SDP:
+ case RT5640_ADDA_CLK1:
+ case RT5640_ADDA_CLK2:
+ case RT5640_DMIC:
+ case RT5640_GLB_CLK:
+ case RT5640_PLL_CTRL1:
+ case RT5640_PLL_CTRL2:
+ case RT5640_ASRC_1:
+ case RT5640_ASRC_2:
+ case RT5640_ASRC_3:
+ case RT5640_ASRC_4:
+ case RT5640_ASRC_5:
+ case RT5640_HP_OVCD:
+ case RT5640_CLS_D_OVCD:
+ case RT5640_CLS_D_OUT:
+ case RT5640_DEPOP_M1:
+ case RT5640_DEPOP_M2:
+ case RT5640_DEPOP_M3:
+ case RT5640_CHARGE_PUMP:
+ case RT5640_PV_DET_SPK_G:
+ case RT5640_MICBIAS:
+ case RT5640_EQ_CTRL1:
+ case RT5640_EQ_CTRL2:
+ case RT5640_WIND_FILTER:
+ case RT5640_DRC_AGC_1:
+ case RT5640_DRC_AGC_2:
+ case RT5640_DRC_AGC_3:
+ case RT5640_SVOL_ZC:
+ case RT5640_ANC_CTRL1:
+ case RT5640_ANC_CTRL2:
+ case RT5640_ANC_CTRL3:
+ case RT5640_JD_CTRL:
+ case RT5640_ANC_JD:
+ case RT5640_IRQ_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_GPIO_CTRL1:
+ case RT5640_GPIO_CTRL2:
+ case RT5640_GPIO_CTRL3:
+ case RT5640_DSP_CTRL1:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_DSP_CTRL4:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR2:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_PGM_REG_ARR4:
+ case RT5640_PGM_REG_ARR5:
+ case RT5640_SCB_FUNC:
+ case RT5640_SCB_CTRL:
+ case RT5640_BASE_BACK:
+ case RT5640_MP3_PLUS1:
+ case RT5640_MP3_PLUS2:
+ case RT5640_3D_HP:
+ case RT5640_ADJ_HPF:
+ case RT5640_HP_CALIB_AMP_DET:
+ case RT5640_HP_CALIB2:
+ case RT5640_SV_ZCD1:
+ case RT5640_SV_ZCD2:
+ case RT5640_DUMMY1:
+ case RT5640_DUMMY2:
+ case RT5640_DUMMY3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5640_data_select[] = {
+ "Normal", "left copy to right", "right copy to left", "Swap"};
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
+
+/* Class D speaker gain ratio */
+static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
+ "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
+ RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
+
+static const struct snd_kcontrol_new rt5640_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_DOUBLE("Speaker Playback Switch", RT5640_SPK_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Playback Switch", RT5640_HP_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* MONO Output Control */
+ SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT,
+ RT5640_L_MUTE_SFT, 1, 1),
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
+ RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 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),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
+ RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL,
+ RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+ /* Class D speaker gain ratio */
+ SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum),
+
+ SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum),
+ SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum),
+ SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int div[] = {2, 3, 4, 6, 8, 12};
+ int idx = -EINVAL, i;
+ int rate, red, bound, temp;
+
+ rate = rt5640->sysclk;
+ red = 3000000 * 12;
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ bound = div[i] * 3000000;
+ if (rate > bound)
+ continue;
+ temp = bound - rate;
+ if (temp < red) {
+ red = temp;
+ idx = i;
+ }
+ }
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ idx << RT5640_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+
+ val = snd_soc_read(source->codec, RT5640_GLB_CLK);
+ val &= RT5640_SCLK_SRC_MASK;
+ if (val == RT5640_SCLK_SRC_PLL1 || val == RT5640_SCLK_SRC_PLL1T)
+ return 1;
+ else
+ return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_L2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_R2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER,
+ 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("BST2 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST4_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER,
+ 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("BST2 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST4_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+static const struct snd_kcontrol_new rt5640_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_RM_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_OM_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_RM_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_OM_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_l_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_SM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_R2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_r_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_SM_L_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_L2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_L_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_BST1_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_SV_R_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_BST1_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC2_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_R2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_L2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_R_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_L_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER,
+ RT5640_M_BST1_MM_SFT, 1, 1),
+};
+
+/* INL/R source */
+static const char * const rt5640_inl_src[] = {
+ "IN2P", "MONOP"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_inl_enum, RT5640_INL_INR_VOL,
+ RT5640_INL_SEL_SFT, rt5640_inl_src);
+
+static const struct snd_kcontrol_new rt5640_inl_mux =
+ SOC_DAPM_ENUM("INL source", rt5640_inl_enum);
+
+static const char * const rt5640_inr_src[] = {
+ "IN2N", "MONON"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_inr_enum, RT5640_INL_INR_VOL,
+ RT5640_INR_SEL_SFT, rt5640_inr_src);
+
+static const struct snd_kcontrol_new rt5640_inr_mux =
+ SOC_DAPM_ENUM("INR source", rt5640_inr_enum);
+
+/* Stereo ADC source */
+static const char * const rt5640_stereo_adc1_src[] = {
+ "DIG MIX", "ADC"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum);
+
+static const char * const rt5640_stereo_adc2_src[] = {
+ "DMIC1", "DMIC2", "DIG MIX"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5640_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADCL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum);
+
+static const char * const rt5640_mono_adc_l2_src[] = {
+ "DMIC L1", "DMIC L2", "Mono DAC MIXL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum);
+
+static const char * const rt5640_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADCR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum);
+
+static const char * const rt5640_mono_adc_r2_src[] = {
+ "DMIC R1", "DMIC R2", "Mono DAC MIXR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum);
+
+/* DAC2 channel source */
+static const char * const rt5640_dac_l2_src[] = {
+ "IF2", "Base L/R"
+};
+
+static int rt5640_dac_l2_values[] = {
+ 0,
+ 3,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dac_l2_enum, RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
+ 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_l2_mux =
+ SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
+
+static const char * const rt5640_dac_r2_src[] = {
+ "IF2",
+};
+
+static int rt5640_dac_r2_values[] = {
+ 0,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dac_r2_enum, RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
+ 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum);
+
+/* digital interface and iis interface map */
+static const char * const rt5640_dai_iis_map[] = {
+ "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2"
+};
+
+static int rt5640_dai_iis_map_values[] = {
+ 0,
+ 5,
+ 6,
+ 7,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dai_iis_map_enum, RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
+ 0x7, rt5640_dai_iis_map, rt5640_dai_iis_map_values);
+
+static const struct snd_kcontrol_new rt5640_dai_mux =
+ SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum);
+
+/* SDI select */
+static const char * const rt5640_sdi_sel[] = {
+ "IF1", "IF2"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
+ RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
+
+static const struct snd_kcontrol_new rt5640_sdi_mux =
+ SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
+
+static int spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
+ 0x0001, 0x0001);
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
+ 0xf000, 0xf000);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
+ 0xf000, 0x0000);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
+ 0x0001, 0x0000);
+ break;
+
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK | RT5640_GP3_PIN_MASK,
+ RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP3_PIN_DMIC1_SDA);
+ snd_soc_update_bits(codec, RT5640_DMIC,
+ RT5640_DMIC_1L_LH_MASK | RT5640_DMIC_1R_LH_MASK |
+ RT5640_DMIC_1_DP_MASK,
+ RT5640_DMIC_1L_LH_FALLING | RT5640_DMIC_1R_LH_RISING |
+ RT5640_DMIC_1_DP_IN1P);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK | RT5640_GP4_PIN_MASK,
+ RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP4_PIN_DMIC2_SDA);
+ snd_soc_update_bits(codec, RT5640_DMIC,
+ RT5640_DMIC_2L_LH_MASK | RT5640_DMIC_2R_LH_MASK |
+ RT5640_DMIC_2_DP_MASK,
+ RT5640_DMIC_2L_LH_FALLING | RT5640_DMIC_2R_LH_RISING |
+ RT5640_DMIC_2_DP_IN1N);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
+ RT5640_PWR_PLL_BIT, 0, NULL, 0),
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
+ RT5640_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2,
+ RT5640_PWR_MB1_BIT, 0, NULL, 0),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ 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),
+ SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC,
+ RT5640_DMIC_1_EN_SFT, 0, rt5640_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC,
+ RT5640_DMIC_2_EN_SFT, 0, rt5640_set_dmic2_event,
+ SND_SOC_DAPM_PRE_PMU),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_R_BIT, 0, NULL, 0),
+ /* IN Mux */
+ SND_SOC_DAPM_MUX("INL Mux", SND_SOC_NOPM, 0, 0, &rt5640_inl_mux),
+ SND_SOC_DAPM_MUX("INR Mux", SND_SOC_NOPM, 0, 0, &rt5640_inr_mux),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
+ rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0,
+ rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_L_BIT, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_R_BIT, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_SF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* ANC */
+ SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)),
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_dac_r2_mux),
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L2_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R2_BIT, 0),
+ /* SPK/OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
+ 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
+ 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+ 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+ 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_SV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_SV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_OV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_OV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_HV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_HV_R_BIT, 0, NULL, 0),
+ /* SPO/HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0,
+ rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)),
+ SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
+ rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
+ SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
+ RT5640_PWR_MA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1,
+ SND_SOC_NOPM, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP R Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
+ SND_SOC_NOPM, 0, spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOLP"),
+ SND_SOC_DAPM_OUTPUT("SPOLN"),
+ SND_SOC_DAPM_OUTPUT("SPORP"),
+ SND_SOC_DAPM_OUTPUT("SPORN"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("MONOP"),
+ SND_SOC_DAPM_OUTPUT("MONON"),
+};
+
+static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+ {"IN1P", NULL, "LDO2"},
+ {"IN2P", NULL, "LDO2"},
+
+ {"DMIC L1", NULL, "DMIC1"},
+ {"DMIC R1", NULL, "DMIC1"},
+ {"DMIC L2", NULL, "DMIC2"},
+ {"DMIC R2", NULL, "DMIC2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST1", NULL, "IN1N"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+
+ {"INL VOL", NULL, "IN2P"},
+ {"INR VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "HPOL Switch", "HPOL"},
+ {"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+ {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
+
+ {"RECMIXR", "HPOR Switch", "HPOR"},
+ {"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+ {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC R", NULL, "RECMIXR"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"},
+ {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"},
+ {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"},
+ {"Stereo ADC L1 Mux", "ADC", "ADC L"},
+ {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"},
+
+ {"Stereo ADC R1 Mux", "ADC", "ADC R"},
+ {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"},
+ {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"},
+ {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"},
+ {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"},
+
+ {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"},
+ {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"},
+ {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "ADCL", "ADC L"},
+
+ {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+ {"Mono ADC R1 Mux", "ADCR", "ADC R"},
+ {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"},
+ {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"},
+ {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+
+ {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
+ {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
+ {"Stereo ADC MIXL", NULL, "Stereo Filter"},
+ {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
+ {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
+ {"Stereo ADC MIXR", NULL, "Stereo Filter"},
+ {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
+ {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
+ {"Mono ADC MIXL", NULL, "Mono Left Filter"},
+ {"Mono Left Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
+ {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
+ {"Mono ADC MIXR", NULL, "Mono Right Filter"},
+ {"Mono Right Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"IF2 ADC L", NULL, "Mono ADC MIXL"},
+ {"IF2 ADC R", NULL, "Mono ADC MIXR"},
+ {"IF1 ADC L", NULL, "Stereo ADC MIXL"},
+ {"IF1 ADC R", NULL, "Stereo ADC MIXR"},
+
+ {"IF1 ADC", NULL, "I2S1"},
+ {"IF1 ADC", NULL, "IF1 ADC L"},
+ {"IF1 ADC", NULL, "IF1 ADC R"},
+ {"IF2 ADC", NULL, "I2S2"},
+ {"IF2 ADC", NULL, "IF2 ADC L"},
+ {"IF2 ADC", NULL, "IF2 ADC R"},
+
+ {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"},
+ {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"},
+ {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"},
+ {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"},
+ {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"},
+ {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"},
+
+ {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"},
+ {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"},
+ {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"},
+ {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"},
+ {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"},
+ {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"},
+
+ {"AIF1TX", NULL, "DAI1 TX Mux"},
+ {"AIF1TX", NULL, "SDI1 TX Mux"},
+ {"AIF2TX", NULL, "DAI2 TX Mux"},
+ {"AIF2TX", NULL, "SDI2 TX Mux"},
+
+ {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"},
+ {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"},
+ {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"},
+ {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"IF1 DAC", NULL, "I2S1"},
+ {"IF1 DAC", NULL, "DAI1 RX Mux"},
+ {"IF2 DAC", NULL, "I2S2"},
+ {"IF2 DAC", NULL, "DAI2 RX Mux"},
+
+ {"IF1 DAC L", NULL, "IF1 DAC"},
+ {"IF1 DAC R", NULL, "IF1 DAC"},
+ {"IF2 DAC L", NULL, "IF2 DAC"},
+ {"IF2 DAC R", NULL, "IF2 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+
+ {"ANC", NULL, "Stereo ADC MIXL"},
+ {"ANC", NULL, "Stereo ADC MIXR"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+ {"DAC L2 Mux", "Base L/R", "Audio DSP"},
+
+ {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo DAC MIXL", "ANC Switch", "ANC"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Stereo DAC MIXR", "ANC Switch", "ANC"},
+
+ {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "PLL1", check_sysclk1_source},
+ {"DAC R1", NULL, "Stereo DAC MIXR"},
+ {"DAC R1", NULL, "PLL1", check_sysclk1_source},
+ {"DAC L2", NULL, "Mono DAC MIXL"},
+ {"DAC L2", NULL, "PLL1", check_sysclk1_source},
+ {"DAC R2", NULL, "Mono DAC MIXR"},
+ {"DAC R2", NULL, "PLL1", check_sysclk1_source},
+
+ {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"SPK MIXL", "INL Switch", "INL VOL"},
+ {"SPK MIXL", "DAC L1 Switch", "DAC L1"},
+ {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
+ {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"},
+ {"SPK MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"SPK MIXR", "INR Switch", "INR VOL"},
+ {"SPK MIXR", "DAC R1 Switch", "DAC R1"},
+ {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
+ {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "INL Switch", "INL VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR Switch", "INR VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"SPKVOL L", NULL, "SPK MIXL"},
+ {"SPKVOL R", NULL, "SPK MIXR"},
+ {"HPOVOL L", NULL, "OUT MIXL"},
+ {"HPOVOL R", NULL, "OUT MIXR"},
+ {"OUTVOL L", NULL, "OUT MIXL"},
+ {"OUTVOL R", NULL, "OUT MIXR"},
+
+ {"SPOL MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOL MIX", "DAC L1 Switch", "DAC L1"},
+ {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"},
+ {"SPOL MIX", "BST1 Switch", "BST1"},
+ {"SPOR MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOR MIX", "BST1 Switch", "BST1"},
+
+ {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
+ {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
+ {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
+ {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
+ {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"Mono MIX", "DAC R2 Switch", "DAC R2"},
+ {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+ {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
+ {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"Mono MIX", "BST1 Switch", "BST1"},
+
+ {"HP L Amp", NULL, "HPO MIX L"},
+ {"HP R Amp", NULL, "HPO MIX R"},
+
+ {"SPOLP", NULL, "SPOL MIX"},
+ {"SPOLN", NULL, "SPOL MIX"},
+ {"SPORP", NULL, "SPOR MIX"},
+ {"SPORN", NULL, "SPOR MIX"},
+
+ {"SPOLP", NULL, "Improve SPK Amp Drv"},
+ {"SPOLN", NULL, "Improve SPK Amp Drv"},
+ {"SPORP", NULL, "Improve SPK Amp Drv"},
+ {"SPORN", NULL, "Improve SPK Amp Drv"},
+
+ {"HPOL", NULL, "Improve HP Amp Drv"},
+ {"HPOR", NULL, "Improve HP Amp Drv"},
+
+ {"HPOL", NULL, "HP L Amp"},
+ {"HPOR", NULL, "HP R Amp"},
+ {"LOUTL", NULL, "LOUT MIX"},
+ {"LOUTR", NULL, "LOUT MIX"},
+ {"MONOP", NULL, "Mono MIX"},
+ {"MONON", NULL, "Mono MIX"},
+ {"MONOP", NULL, "Improve MONO Amp Drv"},
+};
+
+static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+{
+ int ret = 0, val;
+
+ if (codec == NULL)
+ return -EINVAL;
+
+ val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT;
+ switch (dai_id) {
+ case RT5640_AIF1:
+ switch (val) {
+ case RT5640_IF_123:
+ case RT5640_IF_132:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_113:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_312:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ case RT5640_AIF2:
+ switch (val) {
+ case RT5640_IF_231:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_223:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_123:
+ case RT5640_IF_321:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0, val_clk, mask_clk, dai_sel;
+ int pre_div, bclk_ms, frame_size;
+
+ rt5640->lrck[dai->id] = params_rate(params);
+ pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
+ if (pre_div < 0) {
+ dev_err(codec->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ return frame_size;
+ }
+ if (frame_size > 32)
+ bclk_ms = 1;
+ else
+ bclk_ms = 0;
+ rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5640->bclk[dai->id], rt5640->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len |= RT5640_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len |= RT5640_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len |= RT5640_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT |
+ pre_div << RT5640_I2S_PD1_SFT;
+ snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT |
+ pre_div << RT5640_I2S_PD2_SFT;
+ snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0, dai_sel;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5640->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5640_I2S_MS_S;
+ rt5640->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5640_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5640_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5640_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5640_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5640_SCLK_S_MCLK:
+ reg_val |= RT5640_SCLK_SRC_MCLK;
+ break;
+ case RT5640_SCLK_S_PLL1:
+ reg_val |= RT5640_SCLK_SRC_PLL1;
+ break;
+ case RT5640_SCLK_S_PLL1_TK:
+ reg_val |= RT5640_SCLK_SRC_PLL1T;
+ break;
+ case RT5640_SCLK_S_RCCLK:
+ reg_val |= RT5640_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, reg_val);
+ rt5640->sysclk = freq;
+ rt5640->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ return 0;
+}
+
+/**
+ * rt5640_pll_calc - Calculate PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K and bypass flag.
+ *
+ * Calculate M/N/K code to configure PLL for codec. And K is assigned to 2
+ * which make calculation more efficiently.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5640_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt5640_pll_code *pll_code)
+{
+ int max_n = RT5640_PLL_N_MAX, max_m = RT5640_PLL_M_MAX;
+ int n = 0, m = 0, red, n_t, m_t, in_t, out_t;
+ int red_t = abs(freq_out - freq_in);
+ bool bypass = false;
+
+ if (RT5640_PLL_INP_MAX < freq_in || RT5640_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = (freq_in >> 1) + (freq_in >> 2) * n_t;
+ if (in_t < 0)
+ continue;
+ if (in_t == freq_out) {
+ bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / (m_t + 2);
+ red = abs(out_t - freq_out);
+ if (red < red_t) {
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+ pll_code->m_bp = bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = 2;
+ return 0;
+}
+
+static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_pll_code *pll_code = &rt5640->pll_code;
+ int ret, dai_sel;
+
+ if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
+ freq_out == rt5640->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5640->pll_in = 0;
+ rt5640->pll_out = 0;
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5640_PLL1_S_MCLK:
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
+ break;
+ case RT5640_PLL1_S_BCLK1:
+ case RT5640_PLL1_S_BCLK2:
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev,
+ "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
+ }
+ break;
+ default:
+ dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt5640_pll_calc(freq_in, freq_out, pll_code);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp,
+ (pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code);
+
+ snd_soc_write(codec, RT5640_PLL_CTRL1,
+ pll_code->n_code << RT5640_PLL_N_SFT | pll_code->k_code);
+ snd_soc_write(codec, RT5640_PLL_CTRL2,
+ (pll_code->m_bp ? 0 : pll_code->m_code) << RT5640_PLL_M_SFT |
+ pll_code->m_bp << RT5640_PLL_M_BP_SFT);
+
+ rt5640->pll_in = freq_in;
+ rt5640->pll_out = freq_out;
+ rt5640->pll_src = source;
+
+ return 0;
+}
+
+static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ regcache_cache_only(rt5640->regmap, false);
+ snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2);
+ mdelay(10);
+ snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+ regcache_sync(rt5640->regmap);
+ snd_soc_update_bits(codec, RT5640_DUMMY1,
+ 0x0301, 0x0301);
+ snd_soc_update_bits(codec, RT5640_DEPOP_M1,
+ 0x001d, 0x0019);
+ snd_soc_update_bits(codec, RT5640_DEPOP_M2,
+ 0x2000, 0x2000);
+ snd_soc_update_bits(codec, RT5640_MICBIAS,
+ 0x0030, 0x0030);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004);
+ snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100);
+ snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0);
+ snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_VOL, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5640_probe(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ rt5640->codec = codec;
+ codec->control_data = rt5640->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ codec->dapm.idle_bias_off = 1;
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
+ snd_soc_update_bits(codec, RT5640_DEPOP_M1, 0x001d, 0x0019);
+ snd_soc_update_bits(codec, RT5640_DEPOP_M2, 0x2000, 0x2000);
+ snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
+ snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+
+ return 0;
+}
+
+static int rt5640_remove(struct snd_soc_codec *codec)
+{
+ rt5640_reset(codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5640_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ rt5640_reset(codec);
+ regcache_cache_only(rt5640->regmap, true);
+ regcache_mark_dirty(rt5640->regmap);
+
+ return 0;
+}
+
+static int rt5640_resume(struct snd_soc_codec *codec)
+{
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define rt5640_suspend NULL
+#define rt5640_resume NULL
+#endif
+
+#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5640_aif_dai_ops = {
+ .hw_params = rt5640_hw_params,
+ .set_fmt = rt5640_set_dai_fmt,
+ .set_sysclk = rt5640_set_dai_sysclk,
+ .set_pll = rt5640_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5640_dai[] = {
+ {
+ .name = "rt5640-aif1",
+ .id = RT5640_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+ {
+ .name = "rt5640-aif2",
+ .id = RT5640_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
+ .probe = rt5640_probe,
+ .remove = rt5640_remove,
+ .suspend = rt5640_suspend,
+ .resume = rt5640_resume,
+ .set_bias_level = rt5640_set_bias_level,
+ .controls = rt5640_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5640_snd_controls),
+ .dapm_widgets = rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
+ .dapm_routes = rt5640_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+};
+
+static const struct regmap_config rt5640_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) *
+ RT5640_PR_SPACING),
+ .volatile_reg = rt5640_volatile_register,
+ .readable_reg = rt5640_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5640_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5640_reg),
+ .ranges = rt5640_ranges,
+ .num_ranges = ARRAY_SIZE(rt5640_ranges),
+};
+
+static const struct i2c_device_id rt5640_i2c_id[] = {
+ { "rt5640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
+
+static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
+{
+ rt5640->pdata.in1_diff = of_property_read_bool(np,
+ "realtek,in1-differential");
+ rt5640->pdata.in2_diff = of_property_read_bool(np,
+ "realtek,in2-differential");
+
+ rt5640->pdata.ldo1_en = of_get_named_gpio(np,
+ "realtek,ldo1-en-gpios", 0);
+ /*
+ * LDO1_EN is optional (it may be statically tied on the board).
+ * -ENOENT means that the property doesn't exist, i.e. there is no
+ * GPIO, so is not an error. Any other error code means the property
+ * exists, but could not be parsed.
+ */
+ if (!gpio_is_valid(rt5640->pdata.ldo1_en) &&
+ (rt5640->pdata.ldo1_en != -ENOENT))
+ return rt5640->pdata.ldo1_en;
+
+ return 0;
+}
+
+static int rt5640_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5640_priv *rt5640;
+ int ret;
+ unsigned int val;
+
+ rt5640 = devm_kzalloc(&i2c->dev,
+ sizeof(struct rt5640_priv),
+ GFP_KERNEL);
+ if (NULL == rt5640)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c, rt5640);
+
+ if (pdata) {
+ rt5640->pdata = *pdata;
+ /*
+ * Translate zero'd out (default) pdata value to an invalid
+ * GPIO ID. This makes the pdata and DT paths consistent in
+ * terms of the value left in this field when no GPIO is
+ * specified, but means we can't actually use GPIO 0.
+ */
+ if (!rt5640->pdata.ldo1_en)
+ rt5640->pdata.ldo1_en = -EINVAL;
+ } else if (i2c->dev.of_node) {
+ ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
+ if (ret)
+ return ret;
+ } else
+ rt5640->pdata.ldo1_en = -EINVAL;
+
+ rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
+ if (IS_ERR(rt5640->regmap)) {
+ ret = PTR_ERR(rt5640->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
+ ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en,
+ GPIOF_OUT_INIT_HIGH,
+ "RT5640 LDO1_EN");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
+ rt5640->pdata.ldo1_en, ret);
+ return ret;
+ }
+ msleep(400);
+ }
+
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if ((val != RT5640_DEVICE_ID)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5640/39\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5640->regmap, RT5640_RESET, 0);
+
+ ret = regmap_register_patch(rt5640->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5640->pdata.in1_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+ RT5640_IN_DF1, RT5640_IN_DF1);
+
+ if (rt5640->pdata.in2_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
+ rt5640_dai, ARRAY_SIZE(rt5640_dai));
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static int rt5640_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static struct i2c_driver rt5640_i2c_driver = {
+ .driver = {
+ .name = "rt5640",
+ .owner = THIS_MODULE,
+ },
+ .probe = rt5640_i2c_probe,
+ .remove = rt5640_i2c_remove,
+ .id_table = rt5640_i2c_id,
+};
+module_i2c_driver(rt5640_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5640 driver");
+MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
new file mode 100644
index 000000000000..c48286d7118f
--- /dev/null
+++ b/sound/soc/codecs/rt5640.h
@@ -0,0 +1,2092 @@
+/*
+ * rt5640.h -- RT5640 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.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 _RT5640_H
+#define _RT5640_H
+
+#include <sound/rt5640.h>
+
+/* Info */
+#define RT5640_RESET 0x00
+#define RT5640_VENDOR_ID 0xfd
+#define RT5640_VENDOR_ID1 0xfe
+#define RT5640_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5640_SPK_VOL 0x01
+#define RT5640_HP_VOL 0x02
+#define RT5640_OUTPUT 0x03
+#define RT5640_MONO_OUT 0x04
+/* I/O - Input */
+#define RT5640_IN1_IN2 0x0d
+#define RT5640_IN3_IN4 0x0e
+#define RT5640_INL_INR_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5640_DAC1_DIG_VOL 0x19
+#define RT5640_DAC2_DIG_VOL 0x1a
+#define RT5640_DAC2_CTRL 0x1b
+#define RT5640_ADC_DIG_VOL 0x1c
+#define RT5640_ADC_DATA 0x1d
+#define RT5640_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5640_STO_ADC_MIXER 0x27
+#define RT5640_MONO_ADC_MIXER 0x28
+#define RT5640_AD_DA_MIXER 0x29
+#define RT5640_STO_DAC_MIXER 0x2a
+#define RT5640_MONO_DAC_MIXER 0x2b
+#define RT5640_DIG_MIXER 0x2c
+#define RT5640_DSP_PATH1 0x2d
+#define RT5640_DSP_PATH2 0x2e
+#define RT5640_DIG_INF_DATA 0x2f
+/* Mixer - ADC */
+#define RT5640_REC_L1_MIXER 0x3b
+#define RT5640_REC_L2_MIXER 0x3c
+#define RT5640_REC_R1_MIXER 0x3d
+#define RT5640_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5640_HPO_MIXER 0x45
+#define RT5640_SPK_L_MIXER 0x46
+#define RT5640_SPK_R_MIXER 0x47
+#define RT5640_SPO_L_MIXER 0x48
+#define RT5640_SPO_R_MIXER 0x49
+#define RT5640_SPO_CLSD_RATIO 0x4a
+#define RT5640_MONO_MIXER 0x4c
+#define RT5640_OUT_L1_MIXER 0x4d
+#define RT5640_OUT_L2_MIXER 0x4e
+#define RT5640_OUT_L3_MIXER 0x4f
+#define RT5640_OUT_R1_MIXER 0x50
+#define RT5640_OUT_R2_MIXER 0x51
+#define RT5640_OUT_R3_MIXER 0x52
+#define RT5640_LOUT_MIXER 0x53
+/* Power */
+#define RT5640_PWR_DIG1 0x61
+#define RT5640_PWR_DIG2 0x62
+#define RT5640_PWR_ANLG1 0x63
+#define RT5640_PWR_ANLG2 0x64
+#define RT5640_PWR_MIXER 0x65
+#define RT5640_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5640_PRIV_INDEX 0x6a
+#define RT5640_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5640_I2S1_SDP 0x70
+#define RT5640_I2S2_SDP 0x71
+#define RT5640_ADDA_CLK1 0x73
+#define RT5640_ADDA_CLK2 0x74
+#define RT5640_DMIC 0x75
+/* Function - Analog */
+#define RT5640_GLB_CLK 0x80
+#define RT5640_PLL_CTRL1 0x81
+#define RT5640_PLL_CTRL2 0x82
+#define RT5640_ASRC_1 0x83
+#define RT5640_ASRC_2 0x84
+#define RT5640_ASRC_3 0x85
+#define RT5640_ASRC_4 0x89
+#define RT5640_ASRC_5 0x8a
+#define RT5640_HP_OVCD 0x8b
+#define RT5640_CLS_D_OVCD 0x8c
+#define RT5640_CLS_D_OUT 0x8d
+#define RT5640_DEPOP_M1 0x8e
+#define RT5640_DEPOP_M2 0x8f
+#define RT5640_DEPOP_M3 0x90
+#define RT5640_CHARGE_PUMP 0x91
+#define RT5640_PV_DET_SPK_G 0x92
+#define RT5640_MICBIAS 0x93
+/* Function - Digital */
+#define RT5640_EQ_CTRL1 0xb0
+#define RT5640_EQ_CTRL2 0xb1
+#define RT5640_WIND_FILTER 0xb2
+#define RT5640_DRC_AGC_1 0xb4
+#define RT5640_DRC_AGC_2 0xb5
+#define RT5640_DRC_AGC_3 0xb6
+#define RT5640_SVOL_ZC 0xb7
+#define RT5640_ANC_CTRL1 0xb8
+#define RT5640_ANC_CTRL2 0xb9
+#define RT5640_ANC_CTRL3 0xba
+#define RT5640_JD_CTRL 0xbb
+#define RT5640_ANC_JD 0xbc
+#define RT5640_IRQ_CTRL1 0xbd
+#define RT5640_IRQ_CTRL2 0xbe
+#define RT5640_INT_IRQ_ST 0xbf
+#define RT5640_GPIO_CTRL1 0xc0
+#define RT5640_GPIO_CTRL2 0xc1
+#define RT5640_GPIO_CTRL3 0xc2
+#define RT5640_DSP_CTRL1 0xc4
+#define RT5640_DSP_CTRL2 0xc5
+#define RT5640_DSP_CTRL3 0xc6
+#define RT5640_DSP_CTRL4 0xc7
+#define RT5640_PGM_REG_ARR1 0xc8
+#define RT5640_PGM_REG_ARR2 0xc9
+#define RT5640_PGM_REG_ARR3 0xca
+#define RT5640_PGM_REG_ARR4 0xcb
+#define RT5640_PGM_REG_ARR5 0xcc
+#define RT5640_SCB_FUNC 0xcd
+#define RT5640_SCB_CTRL 0xce
+#define RT5640_BASE_BACK 0xcf
+#define RT5640_MP3_PLUS1 0xd0
+#define RT5640_MP3_PLUS2 0xd1
+#define RT5640_3D_HP 0xd2
+#define RT5640_ADJ_HPF 0xd3
+#define RT5640_HP_CALIB_AMP_DET 0xd6
+#define RT5640_HP_CALIB2 0xd7
+#define RT5640_SV_ZCD1 0xd9
+#define RT5640_SV_ZCD2 0xda
+/* Dummy Register */
+#define RT5640_DUMMY1 0xfa
+#define RT5640_DUMMY2 0xfb
+#define RT5640_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5640_3D_SPK 0x63
+#define RT5640_WND_1 0x6c
+#define RT5640_WND_2 0x6d
+#define RT5640_WND_3 0x6e
+#define RT5640_WND_4 0x6f
+#define RT5640_WND_5 0x70
+#define RT5640_WND_8 0x73
+#define RT5640_DIP_SPK_INF 0x75
+#define RT5640_EQ_BW_LOP 0xa0
+#define RT5640_EQ_GN_LOP 0xa1
+#define RT5640_EQ_FC_BP1 0xa2
+#define RT5640_EQ_BW_BP1 0xa3
+#define RT5640_EQ_GN_BP1 0xa4
+#define RT5640_EQ_FC_BP2 0xa5
+#define RT5640_EQ_BW_BP2 0xa6
+#define RT5640_EQ_GN_BP2 0xa7
+#define RT5640_EQ_FC_BP3 0xa8
+#define RT5640_EQ_BW_BP3 0xa9
+#define RT5640_EQ_GN_BP3 0xaa
+#define RT5640_EQ_FC_BP4 0xab
+#define RT5640_EQ_BW_BP4 0xac
+#define RT5640_EQ_GN_BP4 0xad
+#define RT5640_EQ_FC_HIP1 0xae
+#define RT5640_EQ_GN_HIP1 0xaf
+#define RT5640_EQ_FC_HIP2 0xb0
+#define RT5640_EQ_BW_HIP2 0xb1
+#define RT5640_EQ_GN_HIP2 0xb2
+#define RT5640_EQ_PRE_VOL 0xb3
+#define RT5640_EQ_PST_VOL 0xb4
+
+/* global definition */
+#define RT5640_L_MUTE (0x1 << 15)
+#define RT5640_L_MUTE_SFT 15
+#define RT5640_VOL_L_MUTE (0x1 << 14)
+#define RT5640_VOL_L_SFT 14
+#define RT5640_R_MUTE (0x1 << 7)
+#define RT5640_R_MUTE_SFT 7
+#define RT5640_VOL_R_MUTE (0x1 << 6)
+#define RT5640_VOL_R_SFT 6
+#define RT5640_L_VOL_MASK (0x3f << 8)
+#define RT5640_L_VOL_SFT 8
+#define RT5640_R_VOL_MASK (0x3f)
+#define RT5640_R_VOL_SFT 0
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5640_BST_SFT1 12
+#define RT5640_BST_SFT2 8
+#define RT5640_IN_DF1 (0x1 << 7)
+#define RT5640_IN_SFT1 7
+#define RT5640_IN_DF2 (0x1 << 6)
+#define RT5640_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5640_INL_SEL_MASK (0x1 << 15)
+#define RT5640_INL_SEL_SFT 15
+#define RT5640_INL_SEL_IN4P (0x0 << 15)
+#define RT5640_INL_SEL_MONOP (0x1 << 15)
+#define RT5640_INL_VOL_MASK (0x1f << 8)
+#define RT5640_INL_VOL_SFT 8
+#define RT5640_INR_SEL_MASK (0x1 << 7)
+#define RT5640_INR_SEL_SFT 7
+#define RT5640_INR_SEL_IN4N (0x0 << 7)
+#define RT5640_INR_SEL_MONON (0x1 << 7)
+#define RT5640_INR_VOL_MASK (0x1f)
+#define RT5640_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5640_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L1_VOL_SFT 8
+#define RT5640_DAC_R1_VOL_MASK (0xff)
+#define RT5640_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5640_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L2_VOL_SFT 8
+#define RT5640_DAC_R2_VOL_MASK (0xff)
+#define RT5640_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5640_M_DAC_L2_VOL (0x1 << 13)
+#define RT5640_M_DAC_L2_VOL_SFT 13
+#define RT5640_M_DAC_R2_VOL (0x1 << 12)
+#define RT5640_M_DAC_R2_VOL_SFT 12
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5640_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_ADC_L_VOL_SFT 8
+#define RT5640_ADC_R_VOL_MASK (0x7f)
+#define RT5640_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_MONO_ADC_L_VOL_SFT 8
+#define RT5640_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5640_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5640_ADC_L_BST_MASK (0x3 << 14)
+#define RT5640_ADC_L_BST_SFT 14
+#define RT5640_ADC_R_BST_MASK (0x3 << 12)
+#define RT5640_ADC_R_BST_SFT 12
+#define RT5640_ADC_COMP_MASK (0x3 << 10)
+#define RT5640_ADC_COMP_SFT 10
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5640_M_ADC_L1 (0x1 << 14)
+#define RT5640_M_ADC_L1_SFT 14
+#define RT5640_M_ADC_L2 (0x1 << 13)
+#define RT5640_M_ADC_L2_SFT 13
+#define RT5640_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5640_ADC_1_SRC_SFT 12
+#define RT5640_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5640_ADC_2_SRC_MASK (0x3 << 10)
+#define RT5640_ADC_2_SRC_SFT 10
+#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10)
+#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10)
+#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10)
+#define RT5640_M_ADC_R1 (0x1 << 6)
+#define RT5640_M_ADC_R1_SFT 6
+#define RT5640_M_ADC_R2 (0x1 << 5)
+#define RT5640_M_ADC_R2_SFT 5
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5640_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5640_M_MONO_ADC_L1_SFT 14
+#define RT5640_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5640_M_MONO_ADC_L2_SFT 13
+#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5640_MONO_ADC_L1_SRC_SFT 12
+#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10)
+#define RT5640_MONO_ADC_L2_SRC_SFT 10
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10)
+#define RT5640_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5640_M_MONO_ADC_R1_SFT 6
+#define RT5640_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5640_M_MONO_ADC_R2_SFT 5
+#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_SFT 4
+#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2)
+#define RT5640_MONO_ADC_R2_SRC_SFT 2
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5640_M_ADCMIX_L (0x1 << 15)
+#define RT5640_M_ADCMIX_L_SFT 15
+#define RT5640_M_IF1_DAC_L (0x1 << 14)
+#define RT5640_M_IF1_DAC_L_SFT 14
+#define RT5640_M_ADCMIX_R (0x1 << 7)
+#define RT5640_M_ADCMIX_R_SFT 7
+#define RT5640_M_IF1_DAC_R (0x1 << 6)
+#define RT5640_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5640_M_DAC_L1 (0x1 << 14)
+#define RT5640_M_DAC_L1_SFT 14
+#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_STO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2 (0x1 << 12)
+#define RT5640_M_DAC_L2_SFT 12
+#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_STO_L_VOL_SFT 11
+#define RT5640_M_ANC_DAC_L (0x1 << 10)
+#define RT5640_M_ANC_DAC_L_SFT 10
+#define RT5640_M_DAC_R1 (0x1 << 6)
+#define RT5640_M_DAC_R1_SFT 6
+#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_STO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2 (0x1 << 4)
+#define RT5640_M_DAC_R2_SFT 4
+#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_STO_R_VOL_SFT 3
+#define RT5640_M_ANC_DAC_R (0x1 << 2)
+#define RT5640_M_ANC_DAC_R_SFT 2
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5640_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5640_M_DAC_L1_MONO_L_SFT 14
+#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5640_M_DAC_L2_MONO_L_SFT 12
+#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5640_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5640_M_DAC_R2_MONO_L_SFT 10
+#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5640_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5640_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5640_M_DAC_R1_MONO_R_SFT 6
+#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5640_M_DAC_R2_MONO_R_SFT 4
+#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5640_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5640_M_DAC_L2_MONO_R_SFT 2
+#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5640_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5640_M_STO_L_DAC_L (0x1 << 15)
+#define RT5640_M_STO_L_DAC_L_SFT 15
+#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5640_STO_L_DAC_L_VOL_SFT 14
+#define RT5640_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5640_M_DAC_L2_DAC_L_SFT 13
+#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5640_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5640_M_STO_R_DAC_R (0x1 << 11)
+#define RT5640_M_STO_R_DAC_R_SFT 11
+#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5640_STO_R_DAC_R_VOL_SFT 10
+#define RT5640_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5640_M_DAC_R2_DAC_R_SFT 9
+#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5640_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5640_RXDP_SRC_MASK (0x1 << 15)
+#define RT5640_RXDP_SRC_SFT 15
+#define RT5640_RXDP_SRC_NOR (0x0 << 15)
+#define RT5640_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5640_TXDP_SRC_MASK (0x1 << 14)
+#define RT5640_TXDP_SRC_SFT 14
+#define RT5640_TXDP_SRC_NOR (0x0 << 14)
+#define RT5640_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5640_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5640_DAC_L2_SEL_SFT 14
+#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5640_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5640_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5640_DAC_R2_SEL_SFT 12
+#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5640_IF2_ADC_L_SEL_SFT 11
+#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5640_IF2_ADC_R_SEL_SFT 10
+#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5640_RXDC_SEL_MASK (0x3 << 8)
+#define RT5640_RXDC_SEL_SFT 8
+#define RT5640_RXDC_SEL_NOR (0x0 << 8)
+#define RT5640_RXDC_SEL_L2R (0x1 << 8)
+#define RT5640_RXDC_SEL_R2L (0x2 << 8)
+#define RT5640_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5640_RXDP_SEL_MASK (0x3 << 6)
+#define RT5640_RXDP_SEL_SFT 6
+#define RT5640_RXDP_SEL_NOR (0x0 << 6)
+#define RT5640_RXDP_SEL_L2R (0x1 << 6)
+#define RT5640_RXDP_SEL_R2L (0x2 << 6)
+#define RT5640_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5640_TXDC_SEL_MASK (0x3 << 4)
+#define RT5640_TXDC_SEL_SFT 4
+#define RT5640_TXDC_SEL_NOR (0x0 << 4)
+#define RT5640_TXDC_SEL_L2R (0x1 << 4)
+#define RT5640_TXDC_SEL_R2L (0x2 << 4)
+#define RT5640_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5640_TXDP_SEL_MASK (0x3 << 2)
+#define RT5640_TXDP_SEL_SFT 2
+#define RT5640_TXDP_SEL_NOR (0x0 << 2)
+#define RT5640_TXDP_SEL_L2R (0x1 << 2)
+#define RT5640_TXDP_SEL_R2L (0x2 << 2)
+#define RT5640_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SFT 14
+#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14)
+#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12)
+#define RT5640_IF1_ADC_SEL_SFT 12
+#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12)
+#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12)
+#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12)
+#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12)
+#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5640_IF2_DAC_SEL_SFT 10
+#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10)
+#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10)
+#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10)
+#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10)
+#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5640_IF2_ADC_SEL_SFT 8
+#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8)
+#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8)
+#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8)
+#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8)
+#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6)
+#define RT5640_IF3_DAC_SEL_SFT 6
+#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6)
+#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6)
+#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6)
+#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6)
+#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4)
+#define RT5640_IF3_ADC_SEL_SFT 4
+#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4)
+#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4)
+#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4)
+#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5640_G_HP_L_RM_L_SFT 13
+#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_IN_L_RM_L_SFT 10
+#define RT5640_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_L_SFT 7
+#define RT5640_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_L_SFT 4
+#define RT5640_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5640_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_L_SFT 13
+#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_OM_L_RM_L_SFT 10
+#define RT5640_M_HP_L_RM_L (0x1 << 6)
+#define RT5640_M_HP_L_RM_L_SFT 6
+#define RT5640_M_IN_L_RM_L (0x1 << 5)
+#define RT5640_M_IN_L_RM_L_SFT 5
+#define RT5640_M_BST4_RM_L (0x1 << 4)
+#define RT5640_M_BST4_RM_L_SFT 4
+#define RT5640_M_BST3_RM_L (0x1 << 3)
+#define RT5640_M_BST3_RM_L_SFT 3
+#define RT5640_M_BST2_RM_L (0x1 << 2)
+#define RT5640_M_BST2_RM_L_SFT 2
+#define RT5640_M_BST1_RM_L (0x1 << 1)
+#define RT5640_M_BST1_RM_L_SFT 1
+#define RT5640_M_OM_L_RM_L (0x1)
+#define RT5640_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5640_G_HP_R_RM_R_SFT 13
+#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_IN_R_RM_R_SFT 10
+#define RT5640_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_R_SFT 7
+#define RT5640_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_R_SFT 4
+#define RT5640_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5640_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_R_SFT 13
+#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_OM_R_RM_R_SFT 10
+#define RT5640_M_HP_R_RM_R (0x1 << 6)
+#define RT5640_M_HP_R_RM_R_SFT 6
+#define RT5640_M_IN_R_RM_R (0x1 << 5)
+#define RT5640_M_IN_R_RM_R_SFT 5
+#define RT5640_M_BST4_RM_R (0x1 << 4)
+#define RT5640_M_BST4_RM_R_SFT 4
+#define RT5640_M_BST3_RM_R (0x1 << 3)
+#define RT5640_M_BST3_RM_R_SFT 3
+#define RT5640_M_BST2_RM_R (0x1 << 2)
+#define RT5640_M_BST2_RM_R_SFT 2
+#define RT5640_M_BST1_RM_R (0x1 << 1)
+#define RT5640_M_BST1_RM_R_SFT 1
+#define RT5640_M_OM_R_RM_R (0x1)
+#define RT5640_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5640_M_DAC2_HM (0x1 << 15)
+#define RT5640_M_DAC2_HM_SFT 15
+#define RT5640_M_DAC1_HM (0x1 << 14)
+#define RT5640_M_DAC1_HM_SFT 14
+#define RT5640_M_HPVOL_HM (0x1 << 13)
+#define RT5640_M_HPVOL_HM_SFT 13
+#define RT5640_G_HPOMIX_MASK (0x1 << 12)
+#define RT5640_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5640_G_RM_L_SM_L_SFT 14
+#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5640_G_IN_L_SM_L_SFT 12
+#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5640_G_DAC_L1_SM_L_SFT 10
+#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5640_G_DAC_L2_SM_L_SFT 8
+#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5640_G_OM_L_SM_L_SFT 6
+#define RT5640_M_RM_L_SM_L (0x1 << 5)
+#define RT5640_M_RM_L_SM_L_SFT 5
+#define RT5640_M_IN_L_SM_L (0x1 << 4)
+#define RT5640_M_IN_L_SM_L_SFT 4
+#define RT5640_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5640_M_DAC_L1_SM_L_SFT 3
+#define RT5640_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5640_M_DAC_L2_SM_L_SFT 2
+#define RT5640_M_OM_L_SM_L (0x1 << 1)
+#define RT5640_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5640_G_RM_R_SM_R_SFT 14
+#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5640_G_IN_R_SM_R_SFT 12
+#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5640_G_DAC_R1_SM_R_SFT 10
+#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5640_G_DAC_R2_SM_R_SFT 8
+#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5640_G_OM_R_SM_R_SFT 6
+#define RT5640_M_RM_R_SM_R (0x1 << 5)
+#define RT5640_M_RM_R_SM_R_SFT 5
+#define RT5640_M_IN_R_SM_R (0x1 << 4)
+#define RT5640_M_IN_R_SM_R_SFT 4
+#define RT5640_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5640_M_DAC_R1_SM_R_SFT 3
+#define RT5640_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5640_M_DAC_R2_SM_R_SFT 2
+#define RT5640_M_OM_R_SM_R (0x1 << 1)
+#define RT5640_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5640_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5640_M_DAC_R1_SPM_L_SFT 15
+#define RT5640_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5640_M_DAC_L1_SPM_L_SFT 14
+#define RT5640_M_SV_R_SPM_L (0x1 << 13)
+#define RT5640_M_SV_R_SPM_L_SFT 13
+#define RT5640_M_SV_L_SPM_L (0x1 << 12)
+#define RT5640_M_SV_L_SPM_L_SFT 12
+#define RT5640_M_BST1_SPM_L (0x1 << 11)
+#define RT5640_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5640_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5640_M_DAC_R1_SPM_R_SFT 13
+#define RT5640_M_SV_R_SPM_R (0x1 << 12)
+#define RT5640_M_SV_R_SPM_R_SFT 12
+#define RT5640_M_BST1_SPM_R (0x1 << 11)
+#define RT5640_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5640_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5640_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5640_M_DAC_R2_MM (0x1 << 15)
+#define RT5640_M_DAC_R2_MM_SFT 15
+#define RT5640_M_DAC_L2_MM (0x1 << 14)
+#define RT5640_M_DAC_L2_MM_SFT 14
+#define RT5640_M_OV_R_MM (0x1 << 13)
+#define RT5640_M_OV_R_MM_SFT 13
+#define RT5640_M_OV_L_MM (0x1 << 12)
+#define RT5640_M_OV_L_MM_SFT 12
+#define RT5640_M_BST1_MM (0x1 << 11)
+#define RT5640_M_BST1_MM_SFT 11
+#define RT5640_G_MONOMIX_MASK (0x1 << 10)
+#define RT5640_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5640_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5640_G_BST3_OM_L_SFT 13
+#define RT5640_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_L_SFT 10
+#define RT5640_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_L_SFT 7
+#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5640_G_IN_L_OM_L_SFT 4
+#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5640_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5640_G_DAC_R2_OM_L_SFT 13
+#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_DAC_L2_OM_L_SFT 10
+#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5640_M_SM_L_OM_L (0x1 << 8)
+#define RT5640_M_SM_L_OM_L_SFT 8
+#define RT5640_M_BST3_OM_L (0x1 << 7)
+#define RT5640_M_BST3_OM_L_SFT 7
+#define RT5640_M_BST2_OM_L (0x1 << 6)
+#define RT5640_M_BST2_OM_L_SFT 6
+#define RT5640_M_BST1_OM_L (0x1 << 5)
+#define RT5640_M_BST1_OM_L_SFT 5
+#define RT5640_M_IN_L_OM_L (0x1 << 4)
+#define RT5640_M_IN_L_OM_L_SFT 4
+#define RT5640_M_RM_L_OM_L (0x1 << 3)
+#define RT5640_M_RM_L_OM_L_SFT 3
+#define RT5640_M_DAC_R2_OM_L (0x1 << 2)
+#define RT5640_M_DAC_R2_OM_L_SFT 2
+#define RT5640_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5640_M_DAC_L2_OM_L_SFT 1
+#define RT5640_M_DAC_L1_OM_L (0x1)
+#define RT5640_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5640_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5640_G_BST4_OM_R_SFT 13
+#define RT5640_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_R_SFT 10
+#define RT5640_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_R_SFT 7
+#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5640_G_IN_R_OM_R_SFT 4
+#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5640_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5640_G_DAC_L2_OM_R_SFT 13
+#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_DAC_R2_OM_R_SFT 10
+#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5640_M_SM_L_OM_R (0x1 << 8)
+#define RT5640_M_SM_L_OM_R_SFT 8
+#define RT5640_M_BST4_OM_R (0x1 << 7)
+#define RT5640_M_BST4_OM_R_SFT 7
+#define RT5640_M_BST2_OM_R (0x1 << 6)
+#define RT5640_M_BST2_OM_R_SFT 6
+#define RT5640_M_BST1_OM_R (0x1 << 5)
+#define RT5640_M_BST1_OM_R_SFT 5
+#define RT5640_M_IN_R_OM_R (0x1 << 4)
+#define RT5640_M_IN_R_OM_R_SFT 4
+#define RT5640_M_RM_R_OM_R (0x1 << 3)
+#define RT5640_M_RM_R_OM_R_SFT 3
+#define RT5640_M_DAC_L2_OM_R (0x1 << 2)
+#define RT5640_M_DAC_L2_OM_R_SFT 2
+#define RT5640_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5640_M_DAC_R2_OM_R_SFT 1
+#define RT5640_M_DAC_R1_OM_R (0x1)
+#define RT5640_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5640_M_DAC_L1_LM (0x1 << 15)
+#define RT5640_M_DAC_L1_LM_SFT 15
+#define RT5640_M_DAC_R1_LM (0x1 << 14)
+#define RT5640_M_DAC_R1_LM_SFT 14
+#define RT5640_M_OV_L_LM (0x1 << 13)
+#define RT5640_M_OV_L_LM_SFT 13
+#define RT5640_M_OV_R_LM (0x1 << 12)
+#define RT5640_M_OV_R_LM_SFT 12
+#define RT5640_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5640_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5640_PWR_I2S1 (0x1 << 15)
+#define RT5640_PWR_I2S1_BIT 15
+#define RT5640_PWR_I2S2 (0x1 << 14)
+#define RT5640_PWR_I2S2_BIT 14
+#define RT5640_PWR_DAC_L1 (0x1 << 12)
+#define RT5640_PWR_DAC_L1_BIT 12
+#define RT5640_PWR_DAC_R1 (0x1 << 11)
+#define RT5640_PWR_DAC_R1_BIT 11
+#define RT5640_PWR_DAC_L2 (0x1 << 7)
+#define RT5640_PWR_DAC_L2_BIT 7
+#define RT5640_PWR_DAC_R2 (0x1 << 6)
+#define RT5640_PWR_DAC_R2_BIT 6
+#define RT5640_PWR_ADC_L (0x1 << 2)
+#define RT5640_PWR_ADC_L_BIT 2
+#define RT5640_PWR_ADC_R (0x1 << 1)
+#define RT5640_PWR_ADC_R_BIT 1
+#define RT5640_PWR_CLS_D (0x1)
+#define RT5640_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5640_PWR_ADC_SF (0x1 << 15)
+#define RT5640_PWR_ADC_SF_BIT 15
+#define RT5640_PWR_ADC_MF_L (0x1 << 14)
+#define RT5640_PWR_ADC_MF_L_BIT 14
+#define RT5640_PWR_ADC_MF_R (0x1 << 13)
+#define RT5640_PWR_ADC_MF_R_BIT 13
+#define RT5640_PWR_I2S_DSP (0x1 << 12)
+#define RT5640_PWR_I2S_DSP_BIT 12
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5640_PWR_VREF1 (0x1 << 15)
+#define RT5640_PWR_VREF1_BIT 15
+#define RT5640_PWR_FV1 (0x1 << 14)
+#define RT5640_PWR_FV1_BIT 14
+#define RT5640_PWR_MB (0x1 << 13)
+#define RT5640_PWR_MB_BIT 13
+#define RT5640_PWR_LM (0x1 << 12)
+#define RT5640_PWR_LM_BIT 12
+#define RT5640_PWR_BG (0x1 << 11)
+#define RT5640_PWR_BG_BIT 11
+#define RT5640_PWR_MM (0x1 << 10)
+#define RT5640_PWR_MM_BIT 10
+#define RT5640_PWR_MA (0x1 << 8)
+#define RT5640_PWR_MA_BIT 8
+#define RT5640_PWR_HP_L (0x1 << 7)
+#define RT5640_PWR_HP_L_BIT 7
+#define RT5640_PWR_HP_R (0x1 << 6)
+#define RT5640_PWR_HP_R_BIT 6
+#define RT5640_PWR_HA (0x1 << 5)
+#define RT5640_PWR_HA_BIT 5
+#define RT5640_PWR_VREF2 (0x1 << 4)
+#define RT5640_PWR_VREF2_BIT 4
+#define RT5640_PWR_FV2 (0x1 << 3)
+#define RT5640_PWR_FV2_BIT 3
+#define RT5640_PWR_LDO2 (0x1 << 2)
+#define RT5640_PWR_LDO2_BIT 2
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5640_PWR_BST1 (0x1 << 15)
+#define RT5640_PWR_BST1_BIT 15
+#define RT5640_PWR_BST2 (0x1 << 14)
+#define RT5640_PWR_BST2_BIT 14
+#define RT5640_PWR_BST3 (0x1 << 13)
+#define RT5640_PWR_BST3_BIT 13
+#define RT5640_PWR_BST4 (0x1 << 12)
+#define RT5640_PWR_BST4_BIT 12
+#define RT5640_PWR_MB1 (0x1 << 11)
+#define RT5640_PWR_MB1_BIT 11
+#define RT5640_PWR_PLL (0x1 << 9)
+#define RT5640_PWR_PLL_BIT 9
+
+/* Power Management for Mixer (0x65) */
+#define RT5640_PWR_OM_L (0x1 << 15)
+#define RT5640_PWR_OM_L_BIT 15
+#define RT5640_PWR_OM_R (0x1 << 14)
+#define RT5640_PWR_OM_R_BIT 14
+#define RT5640_PWR_SM_L (0x1 << 13)
+#define RT5640_PWR_SM_L_BIT 13
+#define RT5640_PWR_SM_R (0x1 << 12)
+#define RT5640_PWR_SM_R_BIT 12
+#define RT5640_PWR_RM_L (0x1 << 11)
+#define RT5640_PWR_RM_L_BIT 11
+#define RT5640_PWR_RM_R (0x1 << 10)
+#define RT5640_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5640_PWR_SV_L (0x1 << 15)
+#define RT5640_PWR_SV_L_BIT 15
+#define RT5640_PWR_SV_R (0x1 << 14)
+#define RT5640_PWR_SV_R_BIT 14
+#define RT5640_PWR_OV_L (0x1 << 13)
+#define RT5640_PWR_OV_L_BIT 13
+#define RT5640_PWR_OV_R (0x1 << 12)
+#define RT5640_PWR_OV_R_BIT 12
+#define RT5640_PWR_HV_L (0x1 << 11)
+#define RT5640_PWR_HV_L_BIT 11
+#define RT5640_PWR_HV_R (0x1 << 10)
+#define RT5640_PWR_HV_R_BIT 10
+#define RT5640_PWR_IN_L (0x1 << 9)
+#define RT5640_PWR_IN_L_BIT 9
+#define RT5640_PWR_IN_R (0x1 << 8)
+#define RT5640_PWR_IN_R_BIT 8
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */
+#define RT5640_I2S_MS_MASK (0x1 << 15)
+#define RT5640_I2S_MS_SFT 15
+#define RT5640_I2S_MS_M (0x0 << 15)
+#define RT5640_I2S_MS_S (0x1 << 15)
+#define RT5640_I2S_IF_MASK (0x7 << 12)
+#define RT5640_I2S_IF_SFT 12
+#define RT5640_I2S_O_CP_MASK (0x3 << 10)
+#define RT5640_I2S_O_CP_SFT 10
+#define RT5640_I2S_O_CP_OFF (0x0 << 10)
+#define RT5640_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5640_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5640_I2S_I_CP_MASK (0x3 << 8)
+#define RT5640_I2S_I_CP_SFT 8
+#define RT5640_I2S_I_CP_OFF (0x0 << 8)
+#define RT5640_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5640_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5640_I2S_BP_MASK (0x1 << 7)
+#define RT5640_I2S_BP_SFT 7
+#define RT5640_I2S_BP_NOR (0x0 << 7)
+#define RT5640_I2S_BP_INV (0x1 << 7)
+#define RT5640_I2S_DL_MASK (0x3 << 2)
+#define RT5640_I2S_DL_SFT 2
+#define RT5640_I2S_DL_16 (0x0 << 2)
+#define RT5640_I2S_DL_20 (0x1 << 2)
+#define RT5640_I2S_DL_24 (0x2 << 2)
+#define RT5640_I2S_DL_8 (0x3 << 2)
+#define RT5640_I2S_DF_MASK (0x3)
+#define RT5640_I2S_DF_SFT 0
+#define RT5640_I2S_DF_I2S (0x0)
+#define RT5640_I2S_DF_LEFT (0x1)
+#define RT5640_I2S_DF_PCM_A (0x2)
+#define RT5640_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5640_I2S2_SDI_MASK (0x1 << 6)
+#define RT5640_I2S2_SDI_SFT 6
+#define RT5640_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5640_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5640_I2S_BCLK_MS1_SFT 15
+#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5640_I2S_PD1_MASK (0x7 << 12)
+#define RT5640_I2S_PD1_SFT 12
+#define RT5640_I2S_PD1_1 (0x0 << 12)
+#define RT5640_I2S_PD1_2 (0x1 << 12)
+#define RT5640_I2S_PD1_3 (0x2 << 12)
+#define RT5640_I2S_PD1_4 (0x3 << 12)
+#define RT5640_I2S_PD1_6 (0x4 << 12)
+#define RT5640_I2S_PD1_8 (0x5 << 12)
+#define RT5640_I2S_PD1_12 (0x6 << 12)
+#define RT5640_I2S_PD1_16 (0x7 << 12)
+#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5640_I2S_BCLK_MS2_SFT 11
+#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5640_I2S_PD2_MASK (0x7 << 8)
+#define RT5640_I2S_PD2_SFT 8
+#define RT5640_I2S_PD2_1 (0x0 << 8)
+#define RT5640_I2S_PD2_2 (0x1 << 8)
+#define RT5640_I2S_PD2_3 (0x2 << 8)
+#define RT5640_I2S_PD2_4 (0x3 << 8)
+#define RT5640_I2S_PD2_6 (0x4 << 8)
+#define RT5640_I2S_PD2_8 (0x5 << 8)
+#define RT5640_I2S_PD2_12 (0x6 << 8)
+#define RT5640_I2S_PD2_16 (0x7 << 8)
+#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5640_I2S_BCLK_MS3_SFT 7
+#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5640_I2S_PD3_MASK (0x7 << 4)
+#define RT5640_I2S_PD3_SFT 4
+#define RT5640_I2S_PD3_1 (0x0 << 4)
+#define RT5640_I2S_PD3_2 (0x1 << 4)
+#define RT5640_I2S_PD3_3 (0x2 << 4)
+#define RT5640_I2S_PD3_4 (0x3 << 4)
+#define RT5640_I2S_PD3_6 (0x4 << 4)
+#define RT5640_I2S_PD3_8 (0x5 << 4)
+#define RT5640_I2S_PD3_12 (0x6 << 4)
+#define RT5640_I2S_PD3_16 (0x7 << 4)
+#define RT5640_DAC_OSR_MASK (0x3 << 2)
+#define RT5640_DAC_OSR_SFT 2
+#define RT5640_DAC_OSR_128 (0x0 << 2)
+#define RT5640_DAC_OSR_64 (0x1 << 2)
+#define RT5640_DAC_OSR_32 (0x2 << 2)
+#define RT5640_DAC_OSR_16 (0x3 << 2)
+#define RT5640_ADC_OSR_MASK (0x3)
+#define RT5640_ADC_OSR_SFT 0
+#define RT5640_ADC_OSR_128 (0x0)
+#define RT5640_ADC_OSR_64 (0x1)
+#define RT5640_ADC_OSR_32 (0x2)
+#define RT5640_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5640_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5640_DAC_L_OSR_SFT 14
+#define RT5640_DAC_L_OSR_128 (0x0 << 14)
+#define RT5640_DAC_L_OSR_64 (0x1 << 14)
+#define RT5640_DAC_L_OSR_32 (0x2 << 14)
+#define RT5640_DAC_L_OSR_16 (0x3 << 14)
+#define RT5640_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5640_ADC_R_OSR_SFT 12
+#define RT5640_ADC_R_OSR_128 (0x0 << 12)
+#define RT5640_ADC_R_OSR_64 (0x1 << 12)
+#define RT5640_ADC_R_OSR_32 (0x2 << 12)
+#define RT5640_ADC_R_OSR_16 (0x3 << 12)
+#define RT5640_DAHPF_EN (0x1 << 11)
+#define RT5640_DAHPF_EN_SFT 11
+#define RT5640_ADHPF_EN (0x1 << 10)
+#define RT5640_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5640_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5640_DMIC_1_EN_SFT 15
+#define RT5640_DMIC_1_DIS (0x0 << 15)
+#define RT5640_DMIC_1_EN (0x1 << 15)
+#define RT5640_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5640_DMIC_2_EN_SFT 14
+#define RT5640_DMIC_2_DIS (0x0 << 14)
+#define RT5640_DMIC_2_EN (0x1 << 14)
+#define RT5640_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5640_DMIC_1L_LH_SFT 13
+#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5640_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5640_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5640_DMIC_1R_LH_SFT 12
+#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5640_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5640_DMIC_1_DP_MASK (0x1 << 11)
+#define RT5640_DMIC_1_DP_SFT 11
+#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11)
+#define RT5640_DMIC_1_DP_IN1P (0x1 << 11)
+#define RT5640_DMIC_2_DP_MASK (0x1 << 10)
+#define RT5640_DMIC_2_DP_SFT 10
+#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10)
+#define RT5640_DMIC_2_DP_IN1N (0x1 << 10)
+#define RT5640_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5640_DMIC_2L_LH_SFT 9
+#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5640_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5640_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5640_DMIC_2R_LH_SFT 8
+#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5640_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5640_DMIC_CLK_MASK (0x7 << 5)
+#define RT5640_DMIC_CLK_SFT 5
+
+/* Global Clock Control (0x80) */
+#define RT5640_SCLK_SRC_MASK (0x3 << 14)
+#define RT5640_SCLK_SRC_SFT 14
+#define RT5640_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5640_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5640_SCLK_SRC_PLL1T (0x2 << 14)
+#define RT5640_SCLK_SRC_RCCLK (0x3 << 14) /* 15MHz */
+#define RT5640_PLL1_SRC_MASK (0x3 << 12)
+#define RT5640_PLL1_SRC_SFT 12
+#define RT5640_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5640_PLL1_PD_MASK (0x1 << 3)
+#define RT5640_PLL1_PD_SFT 3
+#define RT5640_PLL1_PD_1 (0x0 << 3)
+#define RT5640_PLL1_PD_2 (0x1 << 3)
+
+#define RT5640_PLL_INP_MAX 40000000
+#define RT5640_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5640_PLL_N_MAX 0x1ff
+#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7)
+#define RT5640_PLL_N_SFT 7
+#define RT5640_PLL_K_MAX 0x1f
+#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX)
+#define RT5640_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5640_PLL_M_MAX 0xf
+#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12)
+#define RT5640_PLL_M_SFT 12
+#define RT5640_PLL_M_BP (0x1 << 11)
+#define RT5640_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5640_STO_T_MASK (0x1 << 15)
+#define RT5640_STO_T_SFT 15
+#define RT5640_STO_T_SCLK (0x0 << 15)
+#define RT5640_STO_T_LRCK1 (0x1 << 15)
+#define RT5640_M1_T_MASK (0x1 << 14)
+#define RT5640_M1_T_SFT 14
+#define RT5640_M1_T_I2S2 (0x0 << 14)
+#define RT5640_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5640_I2S2_F_MASK (0x1 << 12)
+#define RT5640_I2S2_F_SFT 12
+#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5640_DMIC_1_M_MASK (0x1 << 9)
+#define RT5640_DMIC_1_M_SFT 9
+#define RT5640_DMIC_1_M_NOR (0x0 << 9)
+#define RT5640_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5640_DMIC_2_M_MASK (0x1 << 8)
+#define RT5640_DMIC_2_M_SFT 8
+#define RT5640_DMIC_2_M_NOR (0x0 << 8)
+#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC Control 2 (0x84) */
+#define RT5640_MDA_L_M_MASK (0x1 << 15)
+#define RT5640_MDA_L_M_SFT 15
+#define RT5640_MDA_L_M_NOR (0x0 << 15)
+#define RT5640_MDA_L_M_ASYN (0x1 << 15)
+#define RT5640_MDA_R_M_MASK (0x1 << 14)
+#define RT5640_MDA_R_M_SFT 14
+#define RT5640_MDA_R_M_NOR (0x0 << 14)
+#define RT5640_MDA_R_M_ASYN (0x1 << 14)
+#define RT5640_MAD_L_M_MASK (0x1 << 13)
+#define RT5640_MAD_L_M_SFT 13
+#define RT5640_MAD_L_M_NOR (0x0 << 13)
+#define RT5640_MAD_L_M_ASYN (0x1 << 13)
+#define RT5640_MAD_R_M_MASK (0x1 << 12)
+#define RT5640_MAD_R_M_SFT 12
+#define RT5640_MAD_R_M_NOR (0x0 << 12)
+#define RT5640_MAD_R_M_ASYN (0x1 << 12)
+#define RT5640_ADC_M_MASK (0x1 << 11)
+#define RT5640_ADC_M_SFT 11
+#define RT5640_ADC_M_NOR (0x0 << 11)
+#define RT5640_ADC_M_ASYN (0x1 << 11)
+#define RT5640_STO_DAC_M_MASK (0x1 << 5)
+#define RT5640_STO_DAC_M_SFT 5
+#define RT5640_STO_DAC_M_NOR (0x0 << 5)
+#define RT5640_STO_DAC_M_ASYN (0x1 << 5)
+#define RT5640_I2S1_R_D_MASK (0x1 << 4)
+#define RT5640_I2S1_R_D_SFT 4
+#define RT5640_I2S1_R_D_DIS (0x0 << 4)
+#define RT5640_I2S1_R_D_EN (0x1 << 4)
+#define RT5640_I2S2_R_D_MASK (0x1 << 3)
+#define RT5640_I2S2_R_D_SFT 3
+#define RT5640_I2S2_R_D_DIS (0x0 << 3)
+#define RT5640_I2S2_R_D_EN (0x1 << 3)
+#define RT5640_PRE_SCLK_MASK (0x3)
+#define RT5640_PRE_SCLK_SFT 0
+#define RT5640_PRE_SCLK_512 (0x0)
+#define RT5640_PRE_SCLK_1024 (0x1)
+#define RT5640_PRE_SCLK_2048 (0x2)
+
+/* ASRC Control 3 (0x85) */
+#define RT5640_I2S1_RATE_MASK (0xf << 12)
+#define RT5640_I2S1_RATE_SFT 12
+#define RT5640_I2S2_RATE_MASK (0xf << 8)
+#define RT5640_I2S2_RATE_SFT 8
+
+/* ASRC Control 4 (0x89) */
+#define RT5640_I2S1_PD_MASK (0x7 << 12)
+#define RT5640_I2S1_PD_SFT 12
+#define RT5640_I2S2_PD_MASK (0x7 << 8)
+#define RT5640_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5640_HP_OVCD_MASK (0x1 << 10)
+#define RT5640_HP_OVCD_SFT 10
+#define RT5640_HP_OVCD_DIS (0x0 << 10)
+#define RT5640_HP_OVCD_EN (0x1 << 10)
+#define RT5640_HP_OC_TH_MASK (0x3 << 8)
+#define RT5640_HP_OC_TH_SFT 8
+#define RT5640_HP_OC_TH_90 (0x0 << 8)
+#define RT5640_HP_OC_TH_105 (0x1 << 8)
+#define RT5640_HP_OC_TH_120 (0x2 << 8)
+#define RT5640_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5640_CLSD_OC_MASK (0x1 << 9)
+#define RT5640_CLSD_OC_SFT 9
+#define RT5640_CLSD_OC_PU (0x0 << 9)
+#define RT5640_CLSD_OC_PD (0x1 << 9)
+#define RT5640_AUTO_PD_MASK (0x1 << 8)
+#define RT5640_AUTO_PD_SFT 8
+#define RT5640_AUTO_PD_DIS (0x0 << 8)
+#define RT5640_AUTO_PD_EN (0x1 << 8)
+#define RT5640_CLSD_OC_TH_MASK (0x3f)
+#define RT5640_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5640_CLSD_RATIO_MASK (0xf << 12)
+#define RT5640_CLSD_RATIO_SFT 12
+#define RT5640_CLSD_OM_MASK (0x1 << 11)
+#define RT5640_CLSD_OM_SFT 11
+#define RT5640_CLSD_OM_MONO (0x0 << 11)
+#define RT5640_CLSD_OM_STO (0x1 << 11)
+#define RT5640_CLSD_SCH_MASK (0x1 << 10)
+#define RT5640_CLSD_SCH_SFT 10
+#define RT5640_CLSD_SCH_L (0x0 << 10)
+#define RT5640_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5640_SMT_TRIG_MASK (0x1 << 15)
+#define RT5640_SMT_TRIG_SFT 15
+#define RT5640_SMT_TRIG_DIS (0x0 << 15)
+#define RT5640_SMT_TRIG_EN (0x1 << 15)
+#define RT5640_HP_L_SMT_MASK (0x1 << 9)
+#define RT5640_HP_L_SMT_SFT 9
+#define RT5640_HP_L_SMT_DIS (0x0 << 9)
+#define RT5640_HP_L_SMT_EN (0x1 << 9)
+#define RT5640_HP_R_SMT_MASK (0x1 << 8)
+#define RT5640_HP_R_SMT_SFT 8
+#define RT5640_HP_R_SMT_DIS (0x0 << 8)
+#define RT5640_HP_R_SMT_EN (0x1 << 8)
+#define RT5640_HP_CD_PD_MASK (0x1 << 7)
+#define RT5640_HP_CD_PD_SFT 7
+#define RT5640_HP_CD_PD_DIS (0x0 << 7)
+#define RT5640_HP_CD_PD_EN (0x1 << 7)
+#define RT5640_RSTN_MASK (0x1 << 6)
+#define RT5640_RSTN_SFT 6
+#define RT5640_RSTN_DIS (0x0 << 6)
+#define RT5640_RSTN_EN (0x1 << 6)
+#define RT5640_RSTP_MASK (0x1 << 5)
+#define RT5640_RSTP_SFT 5
+#define RT5640_RSTP_DIS (0x0 << 5)
+#define RT5640_RSTP_EN (0x1 << 5)
+#define RT5640_HP_CO_MASK (0x1 << 4)
+#define RT5640_HP_CO_SFT 4
+#define RT5640_HP_CO_DIS (0x0 << 4)
+#define RT5640_HP_CO_EN (0x1 << 4)
+#define RT5640_HP_CP_MASK (0x1 << 3)
+#define RT5640_HP_CP_SFT 3
+#define RT5640_HP_CP_PD (0x0 << 3)
+#define RT5640_HP_CP_PU (0x1 << 3)
+#define RT5640_HP_SG_MASK (0x1 << 2)
+#define RT5640_HP_SG_SFT 2
+#define RT5640_HP_SG_DIS (0x0 << 2)
+#define RT5640_HP_SG_EN (0x1 << 2)
+#define RT5640_HP_DP_MASK (0x1 << 1)
+#define RT5640_HP_DP_SFT 1
+#define RT5640_HP_DP_PD (0x0 << 1)
+#define RT5640_HP_DP_PU (0x1 << 1)
+#define RT5640_HP_CB_MASK (0x1)
+#define RT5640_HP_CB_SFT 0
+#define RT5640_HP_CB_PD (0x0)
+#define RT5640_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5640_DEPOP_MASK (0x1 << 13)
+#define RT5640_DEPOP_SFT 13
+#define RT5640_DEPOP_AUTO (0x0 << 13)
+#define RT5640_DEPOP_MAN (0x1 << 13)
+#define RT5640_RAMP_MASK (0x1 << 12)
+#define RT5640_RAMP_SFT 12
+#define RT5640_RAMP_DIS (0x0 << 12)
+#define RT5640_RAMP_EN (0x1 << 12)
+#define RT5640_BPS_MASK (0x1 << 11)
+#define RT5640_BPS_SFT 11
+#define RT5640_BPS_DIS (0x0 << 11)
+#define RT5640_BPS_EN (0x1 << 11)
+#define RT5640_FAST_UPDN_MASK (0x1 << 10)
+#define RT5640_FAST_UPDN_SFT 10
+#define RT5640_FAST_UPDN_DIS (0x0 << 10)
+#define RT5640_FAST_UPDN_EN (0x1 << 10)
+#define RT5640_MRES_MASK (0x3 << 8)
+#define RT5640_MRES_SFT 8
+#define RT5640_MRES_15MO (0x0 << 8)
+#define RT5640_MRES_25MO (0x1 << 8)
+#define RT5640_MRES_35MO (0x2 << 8)
+#define RT5640_MRES_45MO (0x3 << 8)
+#define RT5640_VLO_MASK (0x1 << 7)
+#define RT5640_VLO_SFT 7
+#define RT5640_VLO_3V (0x0 << 7)
+#define RT5640_VLO_32V (0x1 << 7)
+#define RT5640_DIG_DP_MASK (0x1 << 6)
+#define RT5640_DIG_DP_SFT 6
+#define RT5640_DIG_DP_DIS (0x0 << 6)
+#define RT5640_DIG_DP_EN (0x1 << 6)
+#define RT5640_DP_TH_MASK (0x3 << 4)
+#define RT5640_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5640_CP_SYS_MASK (0x7 << 12)
+#define RT5640_CP_SYS_SFT 12
+#define RT5640_CP_FQ1_MASK (0x7 << 8)
+#define RT5640_CP_FQ1_SFT 8
+#define RT5640_CP_FQ2_MASK (0x7 << 4)
+#define RT5640_CP_FQ2_SFT 4
+#define RT5640_CP_FQ3_MASK (0x7)
+#define RT5640_CP_FQ3_SFT 0
+
+/* HPOUT charge pump (0x91) */
+#define RT5640_OSW_L_MASK (0x1 << 11)
+#define RT5640_OSW_L_SFT 11
+#define RT5640_OSW_L_DIS (0x0 << 11)
+#define RT5640_OSW_L_EN (0x1 << 11)
+#define RT5640_OSW_R_MASK (0x1 << 10)
+#define RT5640_OSW_R_SFT 10
+#define RT5640_OSW_R_DIS (0x0 << 10)
+#define RT5640_OSW_R_EN (0x1 << 10)
+#define RT5640_PM_HP_MASK (0x3 << 8)
+#define RT5640_PM_HP_SFT 8
+#define RT5640_PM_HP_LV (0x0 << 8)
+#define RT5640_PM_HP_MV (0x1 << 8)
+#define RT5640_PM_HP_HV (0x2 << 8)
+#define RT5640_IB_HP_MASK (0x3 << 6)
+#define RT5640_IB_HP_SFT 6
+#define RT5640_IB_HP_125IL (0x0 << 6)
+#define RT5640_IB_HP_25IL (0x1 << 6)
+#define RT5640_IB_HP_5IL (0x2 << 6)
+#define RT5640_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5640_PVDD_DET_MASK (0x1 << 15)
+#define RT5640_PVDD_DET_SFT 15
+#define RT5640_PVDD_DET_DIS (0x0 << 15)
+#define RT5640_PVDD_DET_EN (0x1 << 15)
+#define RT5640_SPK_AG_MASK (0x1 << 14)
+#define RT5640_SPK_AG_SFT 14
+#define RT5640_SPK_AG_DIS (0x0 << 14)
+#define RT5640_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5640_MIC1_BS_MASK (0x1 << 15)
+#define RT5640_MIC1_BS_SFT 15
+#define RT5640_MIC1_BS_9AV (0x0 << 15)
+#define RT5640_MIC1_BS_75AV (0x1 << 15)
+#define RT5640_MIC2_BS_MASK (0x1 << 14)
+#define RT5640_MIC2_BS_SFT 14
+#define RT5640_MIC2_BS_9AV (0x0 << 14)
+#define RT5640_MIC2_BS_75AV (0x1 << 14)
+#define RT5640_MIC1_CLK_MASK (0x1 << 13)
+#define RT5640_MIC1_CLK_SFT 13
+#define RT5640_MIC1_CLK_DIS (0x0 << 13)
+#define RT5640_MIC1_CLK_EN (0x1 << 13)
+#define RT5640_MIC2_CLK_MASK (0x1 << 12)
+#define RT5640_MIC2_CLK_SFT 12
+#define RT5640_MIC2_CLK_DIS (0x0 << 12)
+#define RT5640_MIC2_CLK_EN (0x1 << 12)
+#define RT5640_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5640_MIC1_OVCD_SFT 11
+#define RT5640_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5640_MIC1_OVCD_EN (0x1 << 11)
+#define RT5640_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5640_MIC1_OVTH_SFT 9
+#define RT5640_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5640_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5640_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5640_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5640_MIC2_OVCD_SFT 8
+#define RT5640_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5640_MIC2_OVCD_EN (0x1 << 8)
+#define RT5640_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5640_MIC2_OVTH_SFT 6
+#define RT5640_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5640_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5640_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5640_PWR_MB_MASK (0x1 << 5)
+#define RT5640_PWR_MB_SFT 5
+#define RT5640_PWR_MB_PD (0x0 << 5)
+#define RT5640_PWR_MB_PU (0x1 << 5)
+#define RT5640_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5640_PWR_CLK25M_SFT 4
+#define RT5640_PWR_CLK25M_PD (0x0 << 4)
+#define RT5640_PWR_CLK25M_PU (0x1 << 4)
+
+/* EQ Control 1 (0xb0) */
+#define RT5640_EQ_SRC_MASK (0x1 << 15)
+#define RT5640_EQ_SRC_SFT 15
+#define RT5640_EQ_SRC_DAC (0x0 << 15)
+#define RT5640_EQ_SRC_ADC (0x1 << 15)
+#define RT5640_EQ_UPD (0x1 << 14)
+#define RT5640_EQ_UPD_BIT 14
+#define RT5640_EQ_CD_MASK (0x1 << 13)
+#define RT5640_EQ_CD_SFT 13
+#define RT5640_EQ_CD_DIS (0x0 << 13)
+#define RT5640_EQ_CD_EN (0x1 << 13)
+#define RT5640_EQ_DITH_MASK (0x3 << 8)
+#define RT5640_EQ_DITH_SFT 8
+#define RT5640_EQ_DITH_NOR (0x0 << 8)
+#define RT5640_EQ_DITH_LSB (0x1 << 8)
+#define RT5640_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5640_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5640_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5640_EQ_HPF1_M_SFT 8
+#define RT5640_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5640_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5640_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5640_EQ_LPF1_M_SFT 7
+#define RT5640_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5640_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5640_EQ_HPF2_MASK (0x1 << 6)
+#define RT5640_EQ_HPF2_SFT 6
+#define RT5640_EQ_HPF2_DIS (0x0 << 6)
+#define RT5640_EQ_HPF2_EN (0x1 << 6)
+#define RT5640_EQ_HPF1_MASK (0x1 << 5)
+#define RT5640_EQ_HPF1_SFT 5
+#define RT5640_EQ_HPF1_DIS (0x0 << 5)
+#define RT5640_EQ_HPF1_EN (0x1 << 5)
+#define RT5640_EQ_BPF4_MASK (0x1 << 4)
+#define RT5640_EQ_BPF4_SFT 4
+#define RT5640_EQ_BPF4_DIS (0x0 << 4)
+#define RT5640_EQ_BPF4_EN (0x1 << 4)
+#define RT5640_EQ_BPF3_MASK (0x1 << 3)
+#define RT5640_EQ_BPF3_SFT 3
+#define RT5640_EQ_BPF3_DIS (0x0 << 3)
+#define RT5640_EQ_BPF3_EN (0x1 << 3)
+#define RT5640_EQ_BPF2_MASK (0x1 << 2)
+#define RT5640_EQ_BPF2_SFT 2
+#define RT5640_EQ_BPF2_DIS (0x0 << 2)
+#define RT5640_EQ_BPF2_EN (0x1 << 2)
+#define RT5640_EQ_BPF1_MASK (0x1 << 1)
+#define RT5640_EQ_BPF1_SFT 1
+#define RT5640_EQ_BPF1_DIS (0x0 << 1)
+#define RT5640_EQ_BPF1_EN (0x1 << 1)
+#define RT5640_EQ_LPF_MASK (0x1)
+#define RT5640_EQ_LPF_SFT 0
+#define RT5640_EQ_LPF_DIS (0x0)
+#define RT5640_EQ_LPF_EN (0x1)
+
+/* Memory Test (0xb2) */
+#define RT5640_MT_MASK (0x1 << 15)
+#define RT5640_MT_SFT 15
+#define RT5640_MT_DIS (0x0 << 15)
+#define RT5640_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5640_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5640_DRC_AGC_P_SFT 15
+#define RT5640_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5640_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5640_DRC_AGC_MASK (0x1 << 14)
+#define RT5640_DRC_AGC_SFT 14
+#define RT5640_DRC_AGC_DIS (0x0 << 14)
+#define RT5640_DRC_AGC_EN (0x1 << 14)
+#define RT5640_DRC_AGC_UPD (0x1 << 13)
+#define RT5640_DRC_AGC_UPD_BIT 13
+#define RT5640_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5640_DRC_AGC_AR_SFT 8
+#define RT5640_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5640_DRC_AGC_R_SFT 5
+#define RT5640_DRC_AGC_R_48K (0x1 << 5)
+#define RT5640_DRC_AGC_R_96K (0x2 << 5)
+#define RT5640_DRC_AGC_R_192K (0x3 << 5)
+#define RT5640_DRC_AGC_R_441K (0x5 << 5)
+#define RT5640_DRC_AGC_R_882K (0x6 << 5)
+#define RT5640_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5640_DRC_AGC_RC_MASK (0x1f)
+#define RT5640_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5640_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5640_DRC_AGC_POB_SFT 8
+#define RT5640_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5640_DRC_AGC_CP_SFT 7
+#define RT5640_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5640_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5640_DRC_AGC_CPR_SFT 5
+#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5640_DRC_AGC_PRB_MASK (0x1f)
+#define RT5640_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5640_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5640_DRC_AGC_NGB_SFT 12
+#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5640_DRC_AGC_TAR_SFT 7
+#define RT5640_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5640_DRC_AGC_NG_SFT 6
+#define RT5640_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5640_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5640_DRC_AGC_NGH_SFT 5
+#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5640_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5640_DRC_AGC_NGT_MASK (0x1f)
+#define RT5640_DRC_AGC_NGT_SFT 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5640_ANC_M_MASK (0x1 << 15)
+#define RT5640_ANC_M_SFT 15
+#define RT5640_ANC_M_NOR (0x0 << 15)
+#define RT5640_ANC_M_REV (0x1 << 15)
+#define RT5640_ANC_MASK (0x1 << 14)
+#define RT5640_ANC_SFT 14
+#define RT5640_ANC_DIS (0x0 << 14)
+#define RT5640_ANC_EN (0x1 << 14)
+#define RT5640_ANC_MD_MASK (0x3 << 12)
+#define RT5640_ANC_MD_SFT 12
+#define RT5640_ANC_MD_DIS (0x0 << 12)
+#define RT5640_ANC_MD_67MS (0x1 << 12)
+#define RT5640_ANC_MD_267MS (0x2 << 12)
+#define RT5640_ANC_MD_1067MS (0x3 << 12)
+#define RT5640_ANC_SN_MASK (0x1 << 11)
+#define RT5640_ANC_SN_SFT 11
+#define RT5640_ANC_SN_DIS (0x0 << 11)
+#define RT5640_ANC_SN_EN (0x1 << 11)
+#define RT5640_ANC_CLK_MASK (0x1 << 10)
+#define RT5640_ANC_CLK_SFT 10
+#define RT5640_ANC_CLK_ANC (0x0 << 10)
+#define RT5640_ANC_CLK_REG (0x1 << 10)
+#define RT5640_ANC_ZCD_MASK (0x3 << 8)
+#define RT5640_ANC_ZCD_SFT 8
+#define RT5640_ANC_ZCD_DIS (0x0 << 8)
+#define RT5640_ANC_ZCD_T1 (0x1 << 8)
+#define RT5640_ANC_ZCD_T2 (0x2 << 8)
+#define RT5640_ANC_ZCD_WT (0x3 << 8)
+#define RT5640_ANC_CS_MASK (0x1 << 7)
+#define RT5640_ANC_CS_SFT 7
+#define RT5640_ANC_CS_DIS (0x0 << 7)
+#define RT5640_ANC_CS_EN (0x1 << 7)
+#define RT5640_ANC_SW_MASK (0x1 << 6)
+#define RT5640_ANC_SW_SFT 6
+#define RT5640_ANC_SW_NOR (0x0 << 6)
+#define RT5640_ANC_SW_AUTO (0x1 << 6)
+#define RT5640_ANC_CO_L_MASK (0x3f)
+#define RT5640_ANC_CO_L_SFT 0
+
+/* ANC Control 2 (0xb6) */
+#define RT5640_ANC_FG_R_MASK (0xf << 12)
+#define RT5640_ANC_FG_R_SFT 12
+#define RT5640_ANC_FG_L_MASK (0xf << 8)
+#define RT5640_ANC_FG_L_SFT 8
+#define RT5640_ANC_CG_R_MASK (0xf << 4)
+#define RT5640_ANC_CG_R_SFT 4
+#define RT5640_ANC_CG_L_MASK (0xf)
+#define RT5640_ANC_CG_L_SFT 0
+
+/* ANC Control 3 (0xb6) */
+#define RT5640_ANC_CD_MASK (0x1 << 6)
+#define RT5640_ANC_CD_SFT 6
+#define RT5640_ANC_CD_BOTH (0x0 << 6)
+#define RT5640_ANC_CD_IND (0x1 << 6)
+#define RT5640_ANC_CO_R_MASK (0x3f)
+#define RT5640_ANC_CO_R_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5640_JD_MASK (0x7 << 13)
+#define RT5640_JD_SFT 13
+#define RT5640_JD_DIS (0x0 << 13)
+#define RT5640_JD_GPIO1 (0x1 << 13)
+#define RT5640_JD_JD1_IN4P (0x2 << 13)
+#define RT5640_JD_JD2_IN4N (0x3 << 13)
+#define RT5640_JD_GPIO2 (0x4 << 13)
+#define RT5640_JD_GPIO3 (0x5 << 13)
+#define RT5640_JD_GPIO4 (0x6 << 13)
+#define RT5640_JD_HP_MASK (0x1 << 11)
+#define RT5640_JD_HP_SFT 11
+#define RT5640_JD_HP_DIS (0x0 << 11)
+#define RT5640_JD_HP_EN (0x1 << 11)
+#define RT5640_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5640_JD_HP_TRG_SFT 10
+#define RT5640_JD_HP_TRG_LO (0x0 << 10)
+#define RT5640_JD_HP_TRG_HI (0x1 << 10)
+#define RT5640_JD_SPL_MASK (0x1 << 9)
+#define RT5640_JD_SPL_SFT 9
+#define RT5640_JD_SPL_DIS (0x0 << 9)
+#define RT5640_JD_SPL_EN (0x1 << 9)
+#define RT5640_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5640_JD_SPL_TRG_SFT 8
+#define RT5640_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5640_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5640_JD_SPR_MASK (0x1 << 7)
+#define RT5640_JD_SPR_SFT 7
+#define RT5640_JD_SPR_DIS (0x0 << 7)
+#define RT5640_JD_SPR_EN (0x1 << 7)
+#define RT5640_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5640_JD_SPR_TRG_SFT 6
+#define RT5640_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5640_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5640_JD_MO_MASK (0x1 << 5)
+#define RT5640_JD_MO_SFT 5
+#define RT5640_JD_MO_DIS (0x0 << 5)
+#define RT5640_JD_MO_EN (0x1 << 5)
+#define RT5640_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5640_JD_MO_TRG_SFT 4
+#define RT5640_JD_MO_TRG_LO (0x0 << 4)
+#define RT5640_JD_MO_TRG_HI (0x1 << 4)
+#define RT5640_JD_LO_MASK (0x1 << 3)
+#define RT5640_JD_LO_SFT 3
+#define RT5640_JD_LO_DIS (0x0 << 3)
+#define RT5640_JD_LO_EN (0x1 << 3)
+#define RT5640_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5640_JD_LO_TRG_SFT 2
+#define RT5640_JD_LO_TRG_LO (0x0 << 2)
+#define RT5640_JD_LO_TRG_HI (0x1 << 2)
+#define RT5640_JD1_IN4P_MASK (0x1 << 1)
+#define RT5640_JD1_IN4P_SFT 1
+#define RT5640_JD1_IN4P_DIS (0x0 << 1)
+#define RT5640_JD1_IN4P_EN (0x1 << 1)
+#define RT5640_JD2_IN4N_MASK (0x1)
+#define RT5640_JD2_IN4N_SFT 0
+#define RT5640_JD2_IN4N_DIS (0x0)
+#define RT5640_JD2_IN4N_EN (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5640_ANC_DET_MASK (0x3 << 4)
+#define RT5640_ANC_DET_SFT 4
+#define RT5640_ANC_DET_DIS (0x0 << 4)
+#define RT5640_ANC_DET_MB1 (0x1 << 4)
+#define RT5640_ANC_DET_MB2 (0x2 << 4)
+#define RT5640_ANC_DET_JD (0x3 << 4)
+#define RT5640_AD_TRG_MASK (0x1 << 3)
+#define RT5640_AD_TRG_SFT 3
+#define RT5640_AD_TRG_LO (0x0 << 3)
+#define RT5640_AD_TRG_HI (0x1 << 3)
+#define RT5640_ANCM_DET_MASK (0x3 << 4)
+#define RT5640_ANCM_DET_SFT 4
+#define RT5640_ANCM_DET_DIS (0x0 << 4)
+#define RT5640_ANCM_DET_MB1 (0x1 << 4)
+#define RT5640_ANCM_DET_MB2 (0x2 << 4)
+#define RT5640_ANCM_DET_JD (0x3 << 4)
+#define RT5640_AMD_TRG_MASK (0x1 << 3)
+#define RT5640_AMD_TRG_SFT 3
+#define RT5640_AMD_TRG_LO (0x0 << 3)
+#define RT5640_AMD_TRG_HI (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5640_IRQ_JD_MASK (0x1 << 15)
+#define RT5640_IRQ_JD_SFT 15
+#define RT5640_IRQ_JD_BP (0x0 << 15)
+#define RT5640_IRQ_JD_NOR (0x1 << 15)
+#define RT5640_IRQ_OT_MASK (0x1 << 14)
+#define RT5640_IRQ_OT_SFT 14
+#define RT5640_IRQ_OT_BP (0x0 << 14)
+#define RT5640_IRQ_OT_NOR (0x1 << 14)
+#define RT5640_JD_STKY_MASK (0x1 << 13)
+#define RT5640_JD_STKY_SFT 13
+#define RT5640_JD_STKY_DIS (0x0 << 13)
+#define RT5640_JD_STKY_EN (0x1 << 13)
+#define RT5640_OT_STKY_MASK (0x1 << 12)
+#define RT5640_OT_STKY_SFT 12
+#define RT5640_OT_STKY_DIS (0x0 << 12)
+#define RT5640_OT_STKY_EN (0x1 << 12)
+#define RT5640_JD_P_MASK (0x1 << 11)
+#define RT5640_JD_P_SFT 11
+#define RT5640_JD_P_NOR (0x0 << 11)
+#define RT5640_JD_P_INV (0x1 << 11)
+#define RT5640_OT_P_MASK (0x1 << 10)
+#define RT5640_OT_P_SFT 10
+#define RT5640_OT_P_NOR (0x0 << 10)
+#define RT5640_OT_P_INV (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5640_IRQ_MB1_OC_SFT 15
+#define RT5640_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5640_IRQ_MB2_OC_SFT 14
+#define RT5640_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5640_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5640_MB1_OC_STKY_SFT 11
+#define RT5640_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5640_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5640_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5640_MB2_OC_STKY_SFT 10
+#define RT5640_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5640_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5640_MB1_OC_P_MASK (0x1 << 7)
+#define RT5640_MB1_OC_P_SFT 7
+#define RT5640_MB1_OC_P_NOR (0x0 << 7)
+#define RT5640_MB1_OC_P_INV (0x1 << 7)
+#define RT5640_MB2_OC_P_MASK (0x1 << 6)
+#define RT5640_MB2_OC_P_SFT 6
+#define RT5640_MB2_OC_P_NOR (0x0 << 6)
+#define RT5640_MB2_OC_P_INV (0x1 << 6)
+#define RT5640_MB1_OC_CLR (0x1 << 3)
+#define RT5640_MB1_OC_CLR_SFT 3
+#define RT5640_MB2_OC_CLR (0x1 << 2)
+#define RT5640_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5640_GP1_PIN_MASK (0x1 << 15)
+#define RT5640_GP1_PIN_SFT 15
+#define RT5640_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5640_GP1_PIN_IRQ (0x1 << 15)
+#define RT5640_GP2_PIN_MASK (0x1 << 14)
+#define RT5640_GP2_PIN_SFT 14
+#define RT5640_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5640_GP3_PIN_MASK (0x3 << 12)
+#define RT5640_GP3_PIN_SFT 12
+#define RT5640_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5640_GP3_PIN_IRQ (0x2 << 12)
+#define RT5640_GP4_PIN_MASK (0x1 << 11)
+#define RT5640_GP4_PIN_SFT 11
+#define RT5640_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5640_DP_SIG_MASK (0x1 << 10)
+#define RT5640_DP_SIG_SFT 10
+#define RT5640_DP_SIG_TEST (0x0 << 10)
+#define RT5640_DP_SIG_AP (0x1 << 10)
+#define RT5640_GPIO_M_MASK (0x1 << 9)
+#define RT5640_GPIO_M_SFT 9
+#define RT5640_GPIO_M_FLT (0x0 << 9)
+#define RT5640_GPIO_M_PH (0x1 << 9)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5640_GP4_PF_MASK (0x1 << 11)
+#define RT5640_GP4_PF_SFT 11
+#define RT5640_GP4_PF_IN (0x0 << 11)
+#define RT5640_GP4_PF_OUT (0x1 << 11)
+#define RT5640_GP4_OUT_MASK (0x1 << 10)
+#define RT5640_GP4_OUT_SFT 10
+#define RT5640_GP4_OUT_LO (0x0 << 10)
+#define RT5640_GP4_OUT_HI (0x1 << 10)
+#define RT5640_GP4_P_MASK (0x1 << 9)
+#define RT5640_GP4_P_SFT 9
+#define RT5640_GP4_P_NOR (0x0 << 9)
+#define RT5640_GP4_P_INV (0x1 << 9)
+#define RT5640_GP3_PF_MASK (0x1 << 8)
+#define RT5640_GP3_PF_SFT 8
+#define RT5640_GP3_PF_IN (0x0 << 8)
+#define RT5640_GP3_PF_OUT (0x1 << 8)
+#define RT5640_GP3_OUT_MASK (0x1 << 7)
+#define RT5640_GP3_OUT_SFT 7
+#define RT5640_GP3_OUT_LO (0x0 << 7)
+#define RT5640_GP3_OUT_HI (0x1 << 7)
+#define RT5640_GP3_P_MASK (0x1 << 6)
+#define RT5640_GP3_P_SFT 6
+#define RT5640_GP3_P_NOR (0x0 << 6)
+#define RT5640_GP3_P_INV (0x1 << 6)
+#define RT5640_GP2_PF_MASK (0x1 << 5)
+#define RT5640_GP2_PF_SFT 5
+#define RT5640_GP2_PF_IN (0x0 << 5)
+#define RT5640_GP2_PF_OUT (0x1 << 5)
+#define RT5640_GP2_OUT_MASK (0x1 << 4)
+#define RT5640_GP2_OUT_SFT 4
+#define RT5640_GP2_OUT_LO (0x0 << 4)
+#define RT5640_GP2_OUT_HI (0x1 << 4)
+#define RT5640_GP2_P_MASK (0x1 << 3)
+#define RT5640_GP2_P_SFT 3
+#define RT5640_GP2_P_NOR (0x0 << 3)
+#define RT5640_GP2_P_INV (0x1 << 3)
+#define RT5640_GP1_PF_MASK (0x1 << 2)
+#define RT5640_GP1_PF_SFT 2
+#define RT5640_GP1_PF_IN (0x0 << 2)
+#define RT5640_GP1_PF_OUT (0x1 << 2)
+#define RT5640_GP1_OUT_MASK (0x1 << 1)
+#define RT5640_GP1_OUT_SFT 1
+#define RT5640_GP1_OUT_LO (0x0 << 1)
+#define RT5640_GP1_OUT_HI (0x1 << 1)
+#define RT5640_GP1_P_MASK (0x1)
+#define RT5640_GP1_P_SFT 0
+#define RT5640_GP1_P_NOR (0x0)
+#define RT5640_GP1_P_INV (0x1)
+
+/* FM34-500 Register Control 1 (0xc4) */
+#define RT5640_DSP_ADD_SFT 0
+
+/* FM34-500 Register Control 2 (0xc5) */
+#define RT5640_DSP_DAT_SFT 0
+
+/* FM34-500 Register Control 3 (0xc6) */
+#define RT5640_DSP_BUSY_MASK (0x1 << 15)
+#define RT5640_DSP_BUSY_BIT 15
+#define RT5640_DSP_DS_MASK (0x1 << 14)
+#define RT5640_DSP_DS_SFT 14
+#define RT5640_DSP_DS_FM3010 (0x1 << 14)
+#define RT5640_DSP_DS_TEMP (0x1 << 14)
+#define RT5640_DSP_CLK_MASK (0x3 << 12)
+#define RT5640_DSP_CLK_SFT 12
+#define RT5640_DSP_CLK_384K (0x0 << 12)
+#define RT5640_DSP_CLK_192K (0x1 << 12)
+#define RT5640_DSP_CLK_96K (0x2 << 12)
+#define RT5640_DSP_CLK_64K (0x3 << 12)
+#define RT5640_DSP_PD_PIN_MASK (0x1 << 11)
+#define RT5640_DSP_PD_PIN_SFT 11
+#define RT5640_DSP_PD_PIN_LO (0x0 << 11)
+#define RT5640_DSP_PD_PIN_HI (0x1 << 11)
+#define RT5640_DSP_RST_PIN_MASK (0x1 << 10)
+#define RT5640_DSP_RST_PIN_SFT 10
+#define RT5640_DSP_RST_PIN_LO (0x0 << 10)
+#define RT5640_DSP_RST_PIN_HI (0x1 << 10)
+#define RT5640_DSP_R_EN (0x1 << 9)
+#define RT5640_DSP_R_EN_BIT 9
+#define RT5640_DSP_W_EN (0x1 << 8)
+#define RT5640_DSP_W_EN_BIT 8
+#define RT5640_DSP_CMD_MASK (0xff)
+#define RT5640_DSP_CMD_SFT 0
+#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */
+#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */
+#define RT5640_DSP_CMD_RR (0x60) /* Register Read */
+#define RT5640_DSP_CMD_RW (0x68) /* Register Write */
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5640_REG_SEQ_MASK (0xf << 12)
+#define RT5640_REG_SEQ_SFT 12
+#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/
+#define RT5640_SEQ1_ST_SFT 11
+#define RT5640_SEQ1_ST_RUN (0x0 << 11)
+#define RT5640_SEQ1_ST_FIN (0x1 << 11)
+#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/
+#define RT5640_SEQ2_ST_SFT 10
+#define RT5640_SEQ2_ST_RUN (0x0 << 10)
+#define RT5640_SEQ2_ST_FIN (0x1 << 10)
+#define RT5640_REG_LV_MASK (0x1 << 9)
+#define RT5640_REG_LV_SFT 9
+#define RT5640_REG_LV_MX (0x0 << 9)
+#define RT5640_REG_LV_PR (0x1 << 9)
+#define RT5640_SEQ_2_PT_MASK (0x1 << 8)
+#define RT5640_SEQ_2_PT_BIT 8
+#define RT5640_REG_IDX_MASK (0xff)
+#define RT5640_REG_IDX_SFT 0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5640_REG_DAT_MASK (0xffff)
+#define RT5640_REG_DAT_SFT 0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5640_SEQ_DLY_MASK (0xff << 8)
+#define RT5640_SEQ_DLY_SFT 8
+#define RT5640_PROG_MASK (0x1 << 7)
+#define RT5640_PROG_SFT 7
+#define RT5640_PROG_DIS (0x0 << 7)
+#define RT5640_PROG_EN (0x1 << 7)
+#define RT5640_SEQ1_PT_RUN (0x1 << 6)
+#define RT5640_SEQ1_PT_RUN_BIT 6
+#define RT5640_SEQ2_PT_RUN (0x1 << 5)
+#define RT5640_SEQ2_PT_RUN_BIT 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5640_SEQ1_START_MASK (0xf << 8)
+#define RT5640_SEQ1_START_SFT 8
+#define RT5640_SEQ1_END_MASK (0xf)
+#define RT5640_SEQ1_END_SFT 0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5640_SEQ2_START_MASK (0xf << 8)
+#define RT5640_SEQ2_START_SFT 8
+#define RT5640_SEQ2_END_MASK (0xf)
+#define RT5640_SEQ2_END_SFT 0
+
+/* Scramble Function (0xcd) */
+#define RT5640_SCB_KEY_MASK (0xff)
+#define RT5640_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5640_SCB_SWAP_MASK (0x1 << 15)
+#define RT5640_SCB_SWAP_SFT 15
+#define RT5640_SCB_SWAP_DIS (0x0 << 15)
+#define RT5640_SCB_SWAP_EN (0x1 << 15)
+#define RT5640_SCB_MASK (0x1 << 14)
+#define RT5640_SCB_SFT 14
+#define RT5640_SCB_DIS (0x0 << 14)
+#define RT5640_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5640_BB_MASK (0x1 << 15)
+#define RT5640_BB_SFT 15
+#define RT5640_BB_DIS (0x0 << 15)
+#define RT5640_BB_EN (0x1 << 15)
+#define RT5640_BB_CT_MASK (0x7 << 12)
+#define RT5640_BB_CT_SFT 12
+#define RT5640_BB_CT_A (0x0 << 12)
+#define RT5640_BB_CT_B (0x1 << 12)
+#define RT5640_BB_CT_C (0x2 << 12)
+#define RT5640_BB_CT_D (0x3 << 12)
+#define RT5640_M_BB_L_MASK (0x1 << 9)
+#define RT5640_M_BB_L_SFT 9
+#define RT5640_M_BB_R_MASK (0x1 << 8)
+#define RT5640_M_BB_R_SFT 8
+#define RT5640_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5640_M_BB_HPF_L_SFT 7
+#define RT5640_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5640_M_BB_HPF_R_SFT 6
+#define RT5640_G_BB_BST_MASK (0x3f)
+#define RT5640_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5640_M_MP3_L_MASK (0x1 << 15)
+#define RT5640_M_MP3_L_SFT 15
+#define RT5640_M_MP3_R_MASK (0x1 << 14)
+#define RT5640_M_MP3_R_SFT 14
+#define RT5640_M_MP3_MASK (0x1 << 13)
+#define RT5640_M_MP3_SFT 13
+#define RT5640_M_MP3_DIS (0x0 << 13)
+#define RT5640_M_MP3_EN (0x1 << 13)
+#define RT5640_EG_MP3_MASK (0x1f << 8)
+#define RT5640_EG_MP3_SFT 8
+#define RT5640_MP3_HLP_MASK (0x1 << 7)
+#define RT5640_MP3_HLP_SFT 7
+#define RT5640_MP3_HLP_DIS (0x0 << 7)
+#define RT5640_MP3_HLP_EN (0x1 << 7)
+#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5640_M_MP3_ORG_L_SFT 6
+#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5640_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5640_MP3_WT_MASK (0x1 << 13)
+#define RT5640_MP3_WT_SFT 13
+#define RT5640_MP3_WT_1_4 (0x0 << 13)
+#define RT5640_MP3_WT_1_2 (0x1 << 13)
+#define RT5640_OG_MP3_MASK (0x1f << 8)
+#define RT5640_OG_MP3_SFT 8
+#define RT5640_HG_MP3_MASK (0x3f)
+#define RT5640_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5640_3D_CF_MASK (0x1 << 15)
+#define RT5640_3D_CF_SFT 15
+#define RT5640_3D_CF_DIS (0x0 << 15)
+#define RT5640_3D_CF_EN (0x1 << 15)
+#define RT5640_3D_HP_MASK (0x1 << 14)
+#define RT5640_3D_HP_SFT 14
+#define RT5640_3D_HP_DIS (0x0 << 14)
+#define RT5640_3D_HP_EN (0x1 << 14)
+#define RT5640_3D_BT_MASK (0x1 << 13)
+#define RT5640_3D_BT_SFT 13
+#define RT5640_3D_BT_DIS (0x0 << 13)
+#define RT5640_3D_BT_EN (0x1 << 13)
+#define RT5640_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5640_3D_1F_MIX_SFT 11
+#define RT5640_3D_HP_M_MASK (0x1 << 10)
+#define RT5640_3D_HP_M_SFT 10
+#define RT5640_3D_HP_M_SUR (0x0 << 10)
+#define RT5640_3D_HP_M_FRO (0x1 << 10)
+#define RT5640_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5640_M_3D_HRTF_SFT 9
+#define RT5640_M_3D_D2H_MASK (0x1 << 8)
+#define RT5640_M_3D_D2H_SFT 8
+#define RT5640_M_3D_D2R_MASK (0x1 << 7)
+#define RT5640_M_3D_D2R_SFT 7
+#define RT5640_M_3D_REVB_MASK (0x1 << 6)
+#define RT5640_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5640_2ND_HPF_MASK (0x1 << 15)
+#define RT5640_2ND_HPF_SFT 15
+#define RT5640_2ND_HPF_DIS (0x0 << 15)
+#define RT5640_2ND_HPF_EN (0x1 << 15)
+#define RT5640_HPF_CF_L_MASK (0x7 << 12)
+#define RT5640_HPF_CF_L_SFT 12
+#define RT5640_1ST_HPF_MASK (0x1 << 11)
+#define RT5640_1ST_HPF_SFT 11
+#define RT5640_1ST_HPF_DIS (0x0 << 11)
+#define RT5640_1ST_HPF_EN (0x1 << 11)
+#define RT5640_HPF_CF_R_MASK (0x7 << 8)
+#define RT5640_HPF_CF_R_SFT 8
+#define RT5640_ZD_T_MASK (0x3 << 6)
+#define RT5640_ZD_T_SFT 6
+#define RT5640_ZD_F_MASK (0x3 << 4)
+#define RT5640_ZD_F_SFT 4
+#define RT5640_ZD_F_IM (0x0 << 4)
+#define RT5640_ZD_F_ZC_IM (0x1 << 4)
+#define RT5640_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5640_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5640_SI_DAC_MASK (0x1 << 11)
+#define RT5640_SI_DAC_SFT 11
+#define RT5640_SI_DAC_AUTO (0x0 << 11)
+#define RT5640_SI_DAC_TEST (0x1 << 11)
+#define RT5640_DC_CAL_M_MASK (0x1 << 10)
+#define RT5640_DC_CAL_M_SFT 10
+#define RT5640_DC_CAL_M_CAL (0x0 << 10)
+#define RT5640_DC_CAL_M_NOR (0x1 << 10)
+#define RT5640_DC_CAL_MASK (0x1 << 9)
+#define RT5640_DC_CAL_SFT 9
+#define RT5640_DC_CAL_DIS (0x0 << 9)
+#define RT5640_DC_CAL_EN (0x1 << 9)
+#define RT5640_HPD_RCV_MASK (0x7 << 6)
+#define RT5640_HPD_RCV_SFT 6
+#define RT5640_HPD_PS_MASK (0x1 << 5)
+#define RT5640_HPD_PS_SFT 5
+#define RT5640_HPD_PS_DIS (0x0 << 5)
+#define RT5640_HPD_PS_EN (0x1 << 5)
+#define RT5640_CAL_M_MASK (0x1 << 4)
+#define RT5640_CAL_M_SFT 4
+#define RT5640_CAL_M_DEP (0x0 << 4)
+#define RT5640_CAL_M_CAL (0x1 << 4)
+#define RT5640_CAL_MASK (0x1 << 3)
+#define RT5640_CAL_SFT 3
+#define RT5640_CAL_DIS (0x0 << 3)
+#define RT5640_CAL_EN (0x1 << 3)
+#define RT5640_CAL_TEST_MASK (0x1 << 2)
+#define RT5640_CAL_TEST_SFT 2
+#define RT5640_CAL_TEST_DIS (0x0 << 2)
+#define RT5640_CAL_TEST_EN (0x1 << 2)
+#define RT5640_CAL_P_MASK (0x3)
+#define RT5640_CAL_P_SFT 0
+#define RT5640_CAL_P_NONE (0x0)
+#define RT5640_CAL_P_CAL (0x1)
+#define RT5640_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5640_SV_MASK (0x1 << 15)
+#define RT5640_SV_SFT 15
+#define RT5640_SV_DIS (0x0 << 15)
+#define RT5640_SV_EN (0x1 << 15)
+#define RT5640_SPO_SV_MASK (0x1 << 14)
+#define RT5640_SPO_SV_SFT 14
+#define RT5640_SPO_SV_DIS (0x0 << 14)
+#define RT5640_SPO_SV_EN (0x1 << 14)
+#define RT5640_OUT_SV_MASK (0x1 << 13)
+#define RT5640_OUT_SV_SFT 13
+#define RT5640_OUT_SV_DIS (0x0 << 13)
+#define RT5640_OUT_SV_EN (0x1 << 13)
+#define RT5640_HP_SV_MASK (0x1 << 12)
+#define RT5640_HP_SV_SFT 12
+#define RT5640_HP_SV_DIS (0x0 << 12)
+#define RT5640_HP_SV_EN (0x1 << 12)
+#define RT5640_ZCD_DIG_MASK (0x1 << 11)
+#define RT5640_ZCD_DIG_SFT 11
+#define RT5640_ZCD_DIG_DIS (0x0 << 11)
+#define RT5640_ZCD_DIG_EN (0x1 << 11)
+#define RT5640_ZCD_MASK (0x1 << 10)
+#define RT5640_ZCD_SFT 10
+#define RT5640_ZCD_PD (0x0 << 10)
+#define RT5640_ZCD_PU (0x1 << 10)
+#define RT5640_M_ZCD_MASK (0x3f << 4)
+#define RT5640_M_ZCD_SFT 4
+#define RT5640_M_ZCD_RM_L (0x1 << 9)
+#define RT5640_M_ZCD_RM_R (0x1 << 8)
+#define RT5640_M_ZCD_SM_L (0x1 << 7)
+#define RT5640_M_ZCD_SM_R (0x1 << 6)
+#define RT5640_M_ZCD_OM_L (0x1 << 5)
+#define RT5640_M_ZCD_OM_R (0x1 << 4)
+#define RT5640_SV_DLY_MASK (0xf)
+#define RT5640_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5640_ZCD_HP_MASK (0x1 << 15)
+#define RT5640_ZCD_HP_SFT 15
+#define RT5640_ZCD_HP_DIS (0x0 << 15)
+#define RT5640_ZCD_HP_EN (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5640_3D_SPK_MASK (0x1 << 15)
+#define RT5640_3D_SPK_SFT 15
+#define RT5640_3D_SPK_DIS (0x0 << 15)
+#define RT5640_3D_SPK_EN (0x1 << 15)
+#define RT5640_3D_SPK_M_MASK (0x3 << 13)
+#define RT5640_3D_SPK_M_SFT 13
+#define RT5640_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5640_3D_SPK_CG_SFT 8
+#define RT5640_3D_SPK_SG_MASK (0x1f)
+#define RT5640_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5640_WND_MASK (0x1 << 15)
+#define RT5640_WND_SFT 15
+#define RT5640_WND_DIS (0x0 << 15)
+#define RT5640_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5640_WND_FC_NW_MASK (0x3f << 10)
+#define RT5640_WND_FC_NW_SFT 10
+#define RT5640_WND_FC_WK_MASK (0x3f << 4)
+#define RT5640_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5640_HPF_FC_MASK (0x3f << 6)
+#define RT5640_HPF_FC_SFT 6
+#define RT5640_WND_FC_ST_MASK (0x3f)
+#define RT5640_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5640_WND_TH_LO_MASK (0x3ff)
+#define RT5640_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5640_WND_TH_HI_MASK (0x3ff)
+#define RT5640_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5640_WND_WIND_SFT 13
+#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5640_WND_STRONG_SFT 12
+enum {
+ RT5640_NO_WIND,
+ RT5640_BREEZE,
+ RT5640_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5640_DP_ATT_MASK (0x3 << 14)
+#define RT5640_DP_ATT_SFT 14
+#define RT5640_DP_SPK_MASK (0x1 << 10)
+#define RT5640_DP_SPK_SFT 10
+#define RT5640_DP_SPK_DIS (0x0 << 10)
+#define RT5640_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5640_EQ_PRE_VOL_MASK (0xffff)
+#define RT5640_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5640_EQ_PST_VOL_MASK (0xffff)
+#define RT5640_EQ_PST_VOL_SFT 0
+
+#define RT5640_NO_JACK BIT(0)
+#define RT5640_HEADSET_DET BIT(1)
+#define RT5640_HEADPHO_DET BIT(2)
+
+/* System Clock Source */
+#define RT5640_SCLK_S_MCLK 0
+#define RT5640_SCLK_S_PLL1 1
+#define RT5640_SCLK_S_PLL1_TK 2
+#define RT5640_SCLK_S_RCCLK 3
+
+/* PLL1 Source */
+#define RT5640_PLL1_S_MCLK 0
+#define RT5640_PLL1_S_BCLK1 1
+#define RT5640_PLL1_S_BCLK2 2
+#define RT5640_PLL1_S_BCLK3 3
+
+
+enum {
+ RT5640_AIF1,
+ RT5640_AIF2,
+ RT5640_AIF3,
+ RT5640_AIFS,
+};
+
+enum {
+ RT5640_U_IF1 = 0x1,
+ RT5640_U_IF2 = 0x2,
+ RT5640_U_IF3 = 0x4,
+};
+
+enum {
+ RT5640_IF_123,
+ RT5640_IF_132,
+ RT5640_IF_312,
+ RT5640_IF_321,
+ RT5640_IF_231,
+ RT5640_IF_213,
+ RT5640_IF_113,
+ RT5640_IF_223,
+ RT5640_IF_ALL,
+};
+
+enum {
+ RT5640_DMIC_DIS,
+ RT5640_DMIC1,
+ RT5640_DMIC2,
+};
+
+struct rt5640_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+struct rt5640_priv {
+ struct snd_soc_codec *codec;
+ struct rt5640_platform_data pdata;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5640_AIFS];
+ int bclk[RT5640_AIFS];
+ int master[RT5640_AIFS];
+
+ struct rt5640_pll_code pll_code;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int dmic_en;
+};
+
+#endif
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 92bbfec9b107..d441559dc92c 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
+#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
@@ -34,30 +35,30 @@
#define SGTL5000_MAX_REG_OFFSET 0x013A
/* default value of sgtl5000 registers */
-static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = {
- [SGTL5000_CHIP_CLK_CTRL] = 0x0008,
- [SGTL5000_CHIP_I2S_CTRL] = 0x0010,
- [SGTL5000_CHIP_SSS_CTRL] = 0x0008,
- [SGTL5000_CHIP_DAC_VOL] = 0x3c3c,
- [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f,
- [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818,
- [SGTL5000_CHIP_ANA_CTRL] = 0x0111,
- [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404,
- [SGTL5000_CHIP_ANA_POWER] = 0x7060,
- [SGTL5000_CHIP_PLL_CTRL] = 0x5000,
- [SGTL5000_DAP_BASS_ENHANCE] = 0x0040,
- [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f,
- [SGTL5000_DAP_SURROUND] = 0x0040,
- [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f,
- [SGTL5000_DAP_MAIN_CHAN] = 0x8000,
- [SGTL5000_DAP_AVC_CTRL] = 0x0510,
- [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473,
- [SGTL5000_DAP_AVC_ATTACK] = 0x0028,
- [SGTL5000_DAP_AVC_DECAY] = 0x0050,
+static const struct reg_default sgtl5000_reg_defaults[] = {
+ { SGTL5000_CHIP_CLK_CTRL, 0x0008 },
+ { SGTL5000_CHIP_I2S_CTRL, 0x0010 },
+ { SGTL5000_CHIP_SSS_CTRL, 0x0008 },
+ { SGTL5000_CHIP_DAC_VOL, 0x3c3c },
+ { SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
+ { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
+ { SGTL5000_CHIP_ANA_CTRL, 0x0111 },
+ { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
+ { SGTL5000_CHIP_ANA_POWER, 0x7060 },
+ { SGTL5000_CHIP_PLL_CTRL, 0x5000 },
+ { SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
+ { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
+ { SGTL5000_DAP_SURROUND, 0x0040 },
+ { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
+ { SGTL5000_DAP_MAIN_CHAN, 0x8000 },
+ { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
+ { SGTL5000_DAP_AVC_ATTACK, 0x0028 },
+ { SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -112,6 +113,8 @@ struct sgtl5000_priv {
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
struct ldo_regulator *ldo;
+ struct regmap *regmap;
+ struct clk *mclk;
};
/*
@@ -151,12 +154,12 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
break;
- case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, 0);
msleep(400);
@@ -217,12 +220,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0,
- power_vag_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
+
+ SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
+ SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
};
/* routes for sgtl5000 */
@@ -230,16 +232,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
{"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
- {"ADC", NULL, "VAG_POWER"},
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "VAG_POWER"},
{"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
- {"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@@ -909,10 +908,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
if (ret)
return ret;
udelay(10);
+
+ regcache_cache_only(sgtl5000->regmap, false);
+
+ ret = regcache_sync(sgtl5000->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to restore cache: %d\n", ret);
+
+ regcache_cache_only(sgtl5000->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
+
+ return ret;
+ }
}
break;
case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl5000->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
break;
@@ -958,17 +972,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.symmetric_rates = 1,
};
-static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
- return 1;
+ return true;
}
- return 0;
+ return false;
+}
+
+static bool sgtl5000_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_DIG_POWER:
+ case SGTL5000_CHIP_CLK_CTRL:
+ case SGTL5000_CHIP_I2S_CTRL:
+ case SGTL5000_CHIP_SSS_CTRL:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_DAC_VOL:
+ case SGTL5000_CHIP_PAD_STRENGTH:
+ case SGTL5000_CHIP_ANA_ADC_CTRL:
+ case SGTL5000_CHIP_ANA_HP_CTRL:
+ case SGTL5000_CHIP_ANA_CTRL:
+ case SGTL5000_CHIP_LINREG_CTRL:
+ case SGTL5000_CHIP_REF_CTRL:
+ case SGTL5000_CHIP_MIC_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_VOL:
+ case SGTL5000_CHIP_ANA_POWER:
+ case SGTL5000_CHIP_PLL_CTRL:
+ case SGTL5000_CHIP_CLK_TOP_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ case SGTL5000_CHIP_SHORT_CTRL:
+ case SGTL5000_CHIP_ANA_TEST2:
+ case SGTL5000_DAP_CTRL:
+ case SGTL5000_DAP_PEQ:
+ case SGTL5000_DAP_BASS_ENHANCE:
+ case SGTL5000_DAP_BASS_ENHANCE_CTRL:
+ case SGTL5000_DAP_AUDIO_EQ:
+ case SGTL5000_DAP_SURROUND:
+ case SGTL5000_DAP_FLT_COEF_ACCESS:
+ case SGTL5000_DAP_COEF_WR_B0_MSB:
+ case SGTL5000_DAP_COEF_WR_B0_LSB:
+ case SGTL5000_DAP_EQ_BASS_BAND0:
+ case SGTL5000_DAP_EQ_BASS_BAND1:
+ case SGTL5000_DAP_EQ_BASS_BAND2:
+ case SGTL5000_DAP_EQ_BASS_BAND3:
+ case SGTL5000_DAP_EQ_BASS_BAND4:
+ case SGTL5000_DAP_MAIN_CHAN:
+ case SGTL5000_DAP_MIX_CHAN:
+ case SGTL5000_DAP_AVC_CTRL:
+ case SGTL5000_DAP_AVC_THRESHOLD:
+ case SGTL5000_DAP_AVC_ATTACK:
+ case SGTL5000_DAP_AVC_DECAY:
+ case SGTL5000_DAP_COEF_WR_B1_MSB:
+ case SGTL5000_DAP_COEF_WR_B1_LSB:
+ case SGTL5000_DAP_COEF_WR_B2_MSB:
+ case SGTL5000_DAP_COEF_WR_B2_LSB:
+ case SGTL5000_DAP_COEF_WR_A1_MSB:
+ case SGTL5000_DAP_COEF_WR_A1_LSB:
+ case SGTL5000_DAP_COEF_WR_A2_MSB:
+ case SGTL5000_DAP_COEF_WR_A2_LSB:
+ return true;
+
+ default:
+ return false;
+ }
}
#ifdef CONFIG_SUSPEND
@@ -1214,7 +1287,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
{
- u16 reg;
+ int reg;
int ret;
int rev;
int i;
@@ -1242,23 +1315,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
/* wait for all power rails bring up */
udelay(10);
- /* read chip information */
- reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
- if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
- SGTL5000_PARTID_PART_ID) {
- dev_err(codec->dev,
- "Device with ID register %x is not a sgtl5000\n", reg);
- ret = -ENODEV;
- goto err_regulator_disable;
- }
-
- rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
- dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev);
-
/*
* workaround for revision 0x11 and later,
* roll back to use internal LDO
*/
+
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto err_regulator_disable;
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+
if (external_vddd && rev >= 0x11) {
/* disable all regulator first */
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
@@ -1300,7 +1367,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
/* setup i2c data ops */
- ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ codec->control_data = sgtl5000->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -1391,11 +1459,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.suspend = sgtl5000_suspend,
.resume = sgtl5000_resume,
.set_bias_level = sgtl5000_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
- .reg_word_size = sizeof(u16),
- .reg_cache_step = 2,
- .reg_cache_default = sgtl5000_regs,
- .volatile_register = sgtl5000_volatile_register,
.controls = sgtl5000_snd_controls,
.num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
.dapm_widgets = sgtl5000_dapm_widgets,
@@ -1404,28 +1467,114 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
};
+static const struct regmap_config sgtl5000_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = SGTL5000_MAX_REG_OFFSET,
+ .volatile_reg = sgtl5000_volatile,
+ .readable_reg = sgtl5000_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = sgtl5000_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults),
+};
+
+/*
+ * Write all the default values from sgtl5000_reg_defaults[] array into the
+ * sgtl5000 registers, to make sure we always start with the sane registers
+ * values as stated in the datasheet.
+ *
+ * Since sgtl5000 does not have a reset line, nor a reset command in software,
+ * we follow this approach to guarantee we always start from the default values
+ * and avoid problems like, not being able to probe after an audio playback
+ * followed by a system reset or a 'reboot' command in Linux
+ */
+static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+{
+ int i, ret, val, index;
+
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
+ val = sgtl5000_reg_defaults[i].def;
+ index = sgtl5000_reg_defaults[i].reg;
+ ret = regmap_write(sgtl5000->regmap, index, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sgtl5000_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sgtl5000_priv *sgtl5000;
- int ret;
+ int ret, reg, rev;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
+ sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
+ if (IS_ERR(sgtl5000->regmap)) {
+ ret = PTR_ERR(sgtl5000->regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(sgtl5000->mclk)) {
+ ret = PTR_ERR(sgtl5000->mclk);
+ dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sgtl5000->mclk);
+ if (ret)
+ return ret;
+
+ /* read chip information */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto disable_clk;
+
+ if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+ SGTL5000_PARTID_PART_ID) {
+ dev_err(&client->dev,
+ "Device with ID register %x is not a sgtl5000\n", reg);
+ ret = -ENODEV;
+ goto disable_clk;
+ }
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
+
i2c_set_clientdata(client, sgtl5000);
+ /* Ensure sgtl5000 will start with sane register values */
+ ret = sgtl5000_fill_defaults(sgtl5000);
+ if (ret)
+ goto disable_clk;
+
ret = snd_soc_register_codec(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(sgtl5000->mclk);
return ret;
}
static int sgtl5000_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+ snd_soc_unregister_codec(&client->dev);
+ clk_disable_unprepare(sgtl5000->mclk);
return 0;
}
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 8a9f43534b79..4b69229a9818 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -12,7 +12,7 @@
#define _SGTL5000_H
/*
- * Register values.
+ * Registers addresses
*/
#define SGTL5000_CHIP_ID 0x0000
#define SGTL5000_CHIP_DIG_POWER 0x0002
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index 721587c9cd84..73e205c892a0 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -38,9 +38,9 @@ enum si476x_digital_io_output_format {
SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8,
};
-#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
- (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
-#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0b1111110)
+#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
+ (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
+#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e)
enum si476x_daudio_formats {
SI476X_DAUDIO_MODE_I2S = (0x0 << 1),
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index d1ae869d3181..dba26e63844e 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -883,7 +883,7 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec)
return 0;
}
-struct snd_soc_codec_driver sn95031_codec = {
+static struct snd_soc_codec_driver sn95031_codec = {
.probe = sn95031_codec_probe,
.remove = sn95031_codec_remove,
.read = sn95031_read,
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index dd8d856053fc..e9d7881ed2c8 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -21,6 +21,7 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
+#include <linux/of.h>
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
@@ -51,12 +52,21 @@ static int spdif_dir_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id spdif_dir_dt_ids[] = {
+ { .compatible = "linux,spdif-dir", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
+#endif
+
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
.remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spdif_dir_dt_ids),
},
};
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transmitter.c
index 112a49d66e39..18280499fd55 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -20,6 +20,7 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
+#include <linux/of.h>
#define DRV_NAME "spdif-dit"
@@ -52,12 +53,21 @@ static int spdif_dit_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id spdif_dit_dt_ids[] = {
+ { .compatible = "linux,spdif-dit", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
+#endif
+
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spdif_dit_dt_ids),
},
};
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
new file mode 100644
index 000000000000..95aed552139a
--- /dev/null
+++ b/sound/soc/codecs/ssm2518.c
@@ -0,0 +1,856 @@
+/*
+ * SSM2518 amplifier audio driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/ssm2518.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ssm2518.h"
+
+#define SSM2518_REG_POWER1 0x00
+#define SSM2518_REG_CLOCK 0x01
+#define SSM2518_REG_SAI_CTRL1 0x02
+#define SSM2518_REG_SAI_CTRL2 0x03
+#define SSM2518_REG_CHAN_MAP 0x04
+#define SSM2518_REG_LEFT_VOL 0x05
+#define SSM2518_REG_RIGHT_VOL 0x06
+#define SSM2518_REG_MUTE_CTRL 0x07
+#define SSM2518_REG_FAULT_CTRL 0x08
+#define SSM2518_REG_POWER2 0x09
+#define SSM2518_REG_DRC_1 0x0a
+#define SSM2518_REG_DRC_2 0x0b
+#define SSM2518_REG_DRC_3 0x0c
+#define SSM2518_REG_DRC_4 0x0d
+#define SSM2518_REG_DRC_5 0x0e
+#define SSM2518_REG_DRC_6 0x0f
+#define SSM2518_REG_DRC_7 0x10
+#define SSM2518_REG_DRC_8 0x11
+#define SSM2518_REG_DRC_9 0x12
+
+#define SSM2518_POWER1_RESET BIT(7)
+#define SSM2518_POWER1_NO_BCLK BIT(5)
+#define SSM2518_POWER1_MCS_MASK (0xf << 1)
+#define SSM2518_POWER1_MCS_64FS (0x0 << 1)
+#define SSM2518_POWER1_MCS_128FS (0x1 << 1)
+#define SSM2518_POWER1_MCS_256FS (0x2 << 1)
+#define SSM2518_POWER1_MCS_384FS (0x3 << 1)
+#define SSM2518_POWER1_MCS_512FS (0x4 << 1)
+#define SSM2518_POWER1_MCS_768FS (0x5 << 1)
+#define SSM2518_POWER1_MCS_100FS (0x6 << 1)
+#define SSM2518_POWER1_MCS_200FS (0x7 << 1)
+#define SSM2518_POWER1_MCS_400FS (0x8 << 1)
+#define SSM2518_POWER1_SPWDN BIT(0)
+
+#define SSM2518_CLOCK_ASR BIT(0)
+
+#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5)
+#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5)
+#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5)
+
+#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2)
+#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2)
+#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2)
+
+#define SSM2518_SAI_CTRL1_FS_MASK (0x3)
+#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0)
+#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1)
+#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2)
+#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3)
+
+#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7)
+#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6)
+#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5)
+#define SSM2518_SAI_CTRL2_MSB BIT(4)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2)
+#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1)
+
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f
+
+#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5)
+#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0)
+
+#define SSM2518_POWER2_APWDN BIT(0)
+
+#define SSM2518_DAC_MUTE BIT(6)
+#define SSM2518_DAC_FS_MASK 0x07
+#define SSM2518_DAC_FS_8000 0x00
+#define SSM2518_DAC_FS_16000 0x01
+#define SSM2518_DAC_FS_32000 0x02
+#define SSM2518_DAC_FS_64000 0x03
+#define SSM2518_DAC_FS_128000 0x04
+
+struct ssm2518 {
+ struct regmap *regmap;
+ bool right_j;
+
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *constraints;
+
+ int enable_gpio;
+};
+
+static const struct reg_default ssm2518_reg_defaults[] = {
+ { 0x00, 0x05 },
+ { 0x01, 0x00 },
+ { 0x02, 0x02 },
+ { 0x03, 0x00 },
+ { 0x04, 0x10 },
+ { 0x05, 0x40 },
+ { 0x06, 0x40 },
+ { 0x07, 0x81 },
+ { 0x08, 0x0c },
+ { 0x09, 0x99 },
+ { 0x0a, 0x7c },
+ { 0x0b, 0x5b },
+ { 0x0c, 0x57 },
+ { 0x0d, 0x89 },
+ { 0x0e, 0x8c },
+ { 0x0f, 0x77 },
+ { 0x10, 0x26 },
+ { 0x11, 0x1c },
+ { 0x12, 0x97 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400);
+static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+ 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0),
+);
+
+static const char * const ssm2518_drc_peak_detector_attack_time_text[] = {
+ "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms",
+ "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms",
+ "768 ms", "1536 ms",
+};
+
+static const char * const ssm2518_drc_peak_detector_release_time_text[] = {
+ "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms",
+ "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms",
+ "12288 ms", "24576 ms"
+};
+
+static const char * const ssm2518_drc_hold_time_text[] = {
+ "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms",
+ "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms",
+ "682.24 ms", "1364 ms",
+};
+
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
+ SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
+ SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
+ SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
+ SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
+ SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
+ SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
+ SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text);
+
+static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
+ SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL,
+ 4, 1, 0),
+ SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL,
+ SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv),
+ SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1),
+
+ SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0),
+ SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0),
+
+ SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0),
+ SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0),
+ SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0),
+ SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0),
+ SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0),
+
+ SOC_SINGLE_TLV("DRC Limiter Threshold Volume",
+ SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume",
+ SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv),
+ SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4,
+ 4, 15, 1, ssm2518_expander_tlv),
+ SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume",
+ SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Upper Output Threshold Volume",
+ SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Lower Output Threshold Volume",
+ SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8,
+ 2, 15, 1, ssm2518_post_drc_tlv),
+
+ SOC_ENUM("DRC Peak Detector Attack Time",
+ ssm2518_drc_peak_detector_attack_time_enum),
+ SOC_ENUM("DRC Peak Detector Release Time",
+ ssm2518_drc_peak_detector_release_time_enum),
+ SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum),
+ SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum),
+ SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum),
+ SOC_ENUM("DRC Noise Gate Hold Time",
+ ssm2518_drc_noise_gate_hold_time_enum),
+ SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum),
+};
+
+static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1),
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route ssm2518_routes[] = {
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+struct ssm2518_mcs_lut {
+ unsigned int rate;
+ const unsigned int *sysclks;
+};
+
+static const unsigned int ssm2518_sysclks_2048000[] = {
+ 2048000, 4096000, 8192000, 12288000, 16384000, 24576000,
+ 3200000, 6400000, 12800000, 0
+};
+
+static const unsigned int ssm2518_sysclks_2822000[] = {
+ 2822000, 5644800, 11289600, 16934400, 22579200, 33868800,
+ 4410000, 8820000, 17640000, 0
+};
+
+static const unsigned int ssm2518_sysclks_3072000[] = {
+ 3072000, 6144000, 12288000, 16384000, 24576000, 38864000,
+ 4800000, 9600000, 19200000, 0
+};
+
+static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = {
+ { 8000, ssm2518_sysclks_2048000, },
+ { 11025, ssm2518_sysclks_2822000, },
+ { 12000, ssm2518_sysclks_3072000, },
+ { 16000, ssm2518_sysclks_2048000, },
+ { 24000, ssm2518_sysclks_3072000, },
+ { 22050, ssm2518_sysclks_2822000, },
+ { 32000, ssm2518_sysclks_2048000, },
+ { 44100, ssm2518_sysclks_2822000, },
+ { 48000, ssm2518_sysclks_3072000, },
+ { 96000, ssm2518_sysclks_3072000, },
+};
+
+static const unsigned int ssm2518_rates_2048000[] = {
+ 8000, 16000, 32000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = {
+ .list = ssm2518_rates_2048000,
+ .count = ARRAY_SIZE(ssm2518_rates_2048000),
+};
+
+static const unsigned int ssm2518_rates_2822000[] = {
+ 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = {
+ .list = ssm2518_rates_2822000,
+ .count = ARRAY_SIZE(ssm2518_rates_2822000),
+};
+
+static const unsigned int ssm2518_rates_3072000[] = {
+ 12000, 24000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = {
+ .list = ssm2518_rates_3072000,
+ .count = ARRAY_SIZE(ssm2518_rates_3072000),
+};
+
+static const unsigned int ssm2518_rates_12288000[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
+ .list = ssm2518_rates_12288000,
+ .count = ARRAY_SIZE(ssm2518_rates_12288000),
+};
+
+static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
+ unsigned int rate)
+{
+ const unsigned int *sysclks = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) {
+ if (ssm2518_mcs_lut[i].rate == rate) {
+ sysclks = ssm2518_mcs_lut[i].sysclks;
+ break;
+ }
+ }
+
+ if (!sysclks)
+ return -EINVAL;
+
+ for (i = 0; sysclks[i]; i++) {
+ if (sysclks[i] == ssm2518->sysclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ssm2518_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 ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+ unsigned int ctrl1, ctrl1_mask;
+ int mcs;
+ int ret;
+
+ mcs = ssm2518_lookup_mcs(ssm2518, rate);
+ if (mcs < 0)
+ return mcs;
+
+ ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK;
+
+ if (rate >= 8000 && rate <= 12000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000;
+ else
+ return -EINVAL;
+
+ if (ssm2518->right_j) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK;
+ }
+
+ /* Disable auto samplerate detection */
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK,
+ SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ ctrl1_mask, ctrl1);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_MCS_MASK, mcs << 1);
+}
+
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int val;
+
+ if (mute)
+ val = SSM2518_MUTE_CTRL_MUTE_MASTER;
+ else
+ val = 0;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL,
+ SSM2518_MUTE_CTRL_MUTE_MASTER, val);
+}
+
+static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ctrl1 = 0, ctrl2 = 0;
+ bool invert_fclk;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_fclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->right_j = false;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ ssm2518->right_j = true;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_fclk)
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT;
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2);
+}
+
+static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
+{
+ int ret = 0;
+
+ if (!enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN);
+ regcache_mark_dirty(ssm2518->regmap);
+ }
+
+ if (gpio_is_valid(ssm2518->enable_gpio))
+ gpio_set_value(ssm2518->enable_gpio, enable);
+
+ regcache_cache_only(ssm2518->regmap, !enable);
+
+ if (enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00);
+ regcache_sync(ssm2518->regmap);
+ }
+
+ return ret;
+}
+
+static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ ret = ssm2518_set_power(ssm2518, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = ssm2518_set_power(ssm2518, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ctrl1, ctrl2;
+ int left_slot, right_slot;
+ int ret;
+
+ if (slots == 0)
+ return regmap_update_bits(ssm2518->regmap,
+ SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK,
+ SSM2518_SAI_CTRL1_SAI_I2S);
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ /* We assume the left channel < right channel */
+ left_slot = ffs(tx_mask);
+ tx_mask &= ~(1 << tx_mask);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = ffs(tx_mask);
+ tx_mask &= ~(1 << tx_mask);
+ }
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16;
+ break;
+ case 24:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24;
+ break;
+ case 32:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 1:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO;
+ break;
+ case 2:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2;
+ break;
+ case 4:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4;
+ break;
+ case 8:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8;
+ break;
+ case 16:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP,
+ (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) |
+ (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ SSM2518_SAI_CTRL1_SAI_MASK, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2,
+ SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2);
+}
+
+static int ssm2518_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (ssm2518->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints);
+
+ return 0;
+}
+
+#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+
+static const struct snd_soc_dai_ops ssm2518_dai_ops = {
+ .startup = ssm2518_startup,
+ .hw_params = ssm2518_hw_params,
+ .digital_mute = ssm2518_mute,
+ .set_fmt = ssm2518_set_dai_fmt,
+ .set_tdm_slot = ssm2518_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ssm2518_dai = {
+ .name = "ssm2518-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SSM2518_FORMATS,
+ },
+ .ops = &ssm2518_dai_ops,
+};
+
+static int ssm2518_probe(struct snd_soc_codec *codec)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = ssm2518->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int ssm2518_remove(struct snd_soc_codec *codec)
+{
+ ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (clk_id != SSM2518_SYSCLK)
+ return -EINVAL;
+
+ switch (source) {
+ case SSM2518_SYSCLK_SRC_MCLK:
+ val = 0;
+ break;
+ case SSM2518_SYSCLK_SRC_BCLK:
+ /* In this case the bitclock is used as the system clock, and
+ * the bitclock signal needs to be connected to the MCLK pin and
+ * the BCLK pin is left unconnected */
+ val = SSM2518_POWER1_NO_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 0:
+ ssm2518->constraints = NULL;
+ break;
+ case 2048000:
+ case 4096000:
+ case 8192000:
+ case 3200000:
+ case 6400000:
+ case 12800000:
+ ssm2518->constraints = &ssm2518_constraints_2048000;
+ break;
+ case 2822000:
+ case 5644800:
+ case 11289600:
+ case 16934400:
+ case 22579200:
+ case 33868800:
+ case 4410000:
+ case 8820000:
+ case 17640000:
+ ssm2518->constraints = &ssm2518_constraints_2822000;
+ break;
+ case 3072000:
+ case 6144000:
+ case 38864000:
+ case 4800000:
+ case 9600000:
+ case 19200000:
+ ssm2518->constraints = &ssm2518_constraints_3072000;
+ break;
+ case 12288000:
+ case 16384000:
+ case 24576000:
+ ssm2518->constraints = &ssm2518_constraints_12288000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->sysclk = freq;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_NO_BCLK, val);
+}
+
+static struct snd_soc_codec_driver ssm2518_codec_driver = {
+ .probe = ssm2518_probe,
+ .remove = ssm2518_remove,
+ .set_bias_level = ssm2518_set_bias_level,
+ .set_sysclk = ssm2518_set_sysclk,
+ .idle_bias_off = true,
+
+ .controls = ssm2518_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
+ .dapm_widgets = ssm2518_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
+ .dapm_routes = ssm2518_routes,
+ .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,
+ .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
+};
+
+static int ssm2518_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
+ struct ssm2518 *ssm2518;
+ int ret;
+
+ ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL);
+ if (ssm2518 == NULL)
+ return -ENOMEM;
+
+ if (pdata) {
+ ssm2518->enable_gpio = pdata->enable_gpio;
+ } else if (i2c->dev.of_node) {
+ ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
+ if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
+ return ssm2518->enable_gpio;
+ } else {
+ ssm2518->enable_gpio = -1;
+ }
+
+ if (gpio_is_valid(ssm2518->enable_gpio)) {
+ ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
+ GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, ssm2518);
+
+ ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config);
+ if (IS_ERR(ssm2518->regmap))
+ return PTR_ERR(ssm2518->regmap);
+
+ /*
+ * The reset bit is obviously volatile, but we need to be able to cache
+ * the other bits in the register, so we can't just mark the whole
+ * register as volatile. Since this is the only place where we'll ever
+ * touch the reset bit just bypass the cache for this operation.
+ */
+ regcache_cache_bypass(ssm2518->regmap, true);
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_RESET);
+ regcache_cache_bypass(ssm2518->regmap, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2,
+ SSM2518_POWER2_APWDN, 0x00);
+ if (ret)
+ return ret;
+
+ ret = ssm2518_set_power(ssm2518, false);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ &ssm2518_dai, 1);
+}
+
+static int ssm2518_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id ssm2518_i2c_ids[] = {
+ { "ssm2518", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
+
+static struct i2c_driver ssm2518_driver = {
+ .driver = {
+ .name = "ssm2518",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2518_i2c_probe,
+ .remove = ssm2518_i2c_remove,
+ .id_table = ssm2518_i2c_ids,
+};
+module_i2c_driver(ssm2518_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2518 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h
new file mode 100644
index 000000000000..62511d80518e
--- /dev/null
+++ b/sound/soc/codecs/ssm2518.h
@@ -0,0 +1,20 @@
+/*
+ * SSM2518 amplifier audio driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __SND_SOC_CODECS_SSM2518_H__
+#define __SND_SOC_CODECS_SSM2518_H__
+
+#define SSM2518_SYSCLK 0
+
+enum ssm2518_sysclk_src {
+ SSM2518_SYSCLK_SRC_MCLK = 0,
+ SSM2518_SYSCLK_SRC_BCLK = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 2eda85ba79ac..a5455c1aea42 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -28,8 +28,6 @@
#include "stac9766.h"
-#define STAC9766_VERSION "0.10"
-
/*
* STAC9766 register cache
*/
@@ -145,14 +143,14 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
cache[reg / 2] = val;
return 0;
}
@@ -164,7 +162,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
+ val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
@@ -175,7 +173,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
reg == AC97_VENDOR_ID2) {
- val = soc_ac97_ops.read(codec->ac97, reg);
+ val = soc_ac97_ops->read(codec->ac97, reg);
return val;
}
return cache[reg / 2];
@@ -242,15 +240,15 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
return -EIO;
return 0;
@@ -274,7 +272,7 @@ reset:
return -EIO;
}
codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+ id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
if (id != 0x4c13) {
stac9766_reset(codec, 0);
reset++;
@@ -338,9 +336,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d447c4aa1d5e..6d31d88f7204 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -83,6 +83,14 @@
#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */
#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */
#define TAS5086_BKNDERR 0x1c
+#define TAS5086_INPUT_MUX 0x20
+#define TAS5086_PWM_OUTPUT_MUX 0x25
+
+#define TAS5086_MAX_REGISTER TAS5086_PWM_OUTPUT_MUX
+
+#define TAS5086_PWM_START_MIDZ_FOR_START_1 (1 << 7)
+#define TAS5086_PWM_START_MIDZ_FOR_START_2 (1 << 6)
+#define TAS5086_PWM_START_CHANNEL_MASK (0x3f)
/*
* Default TAS5086 power-up configuration
@@ -119,9 +127,30 @@ static const struct reg_default tas5086_reg_defaults[] = {
{ 0x1c, 0x05 },
};
+static int tas5086_register_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
+ return 1;
+ case TAS5086_INPUT_MUX:
+ case TAS5086_PWM_OUTPUT_MUX:
+ return 4;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
{
- return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17));
+ switch (reg) {
+ case 0x0f:
+ case 0x11 ... 0x17:
+ case 0x1d ... 0x1f:
+ return false;
+ default:
+ return true;
+ }
}
static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
@@ -140,6 +169,76 @@ static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
}
+static int tas5086_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i, size;
+ uint8_t buf[5];
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg;
+
+ for (i = size; i >= 1; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 1);
+ if (ret == size + 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int tas5086_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ uint8_t send_buf, recv_buf[4];
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ unsigned int i;
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
struct tas5086_private {
struct regmap *regmap;
unsigned int mclk, sclk;
@@ -376,6 +475,202 @@ static const struct snd_kcontrol_new tas5086_controls[] = {
tas5086_get_deemph, tas5086_put_deemph),
};
+/* Input mux controls */
+static const char *tas5086_dapm_sdin_texts[] =
+{
+ "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R",
+ "SDIN3-L", "SDIN3-R", "Ground (0)", "nc"
+};
+
+static const struct soc_enum tas5086_dapm_input_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0, 8, tas5086_dapm_sdin_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = {
+ SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]),
+ SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]),
+ SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]),
+ SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]),
+ SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]),
+ SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]),
+};
+
+/* Output mux controls */
+static const char *tas5086_dapm_channel_texts[] =
+ { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux",
+ "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" };
+
+static const struct soc_enum tas5086_dapm_output_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0, 6, tas5086_dapm_channel_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = {
+ SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]),
+ SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]),
+ SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]),
+ SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]),
+ SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]),
+ SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]),
+};
+
+static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("SDIN1-L"),
+ SND_SOC_DAPM_INPUT("SDIN1-R"),
+ SND_SOC_DAPM_INPUT("SDIN2-L"),
+ SND_SOC_DAPM_INPUT("SDIN2-R"),
+ SND_SOC_DAPM_INPUT("SDIN3-L"),
+ SND_SOC_DAPM_INPUT("SDIN3-R"),
+ SND_SOC_DAPM_INPUT("SDIN4-L"),
+ SND_SOC_DAPM_INPUT("SDIN4-R"),
+
+ SND_SOC_DAPM_OUTPUT("PWM1"),
+ SND_SOC_DAPM_OUTPUT("PWM2"),
+ SND_SOC_DAPM_OUTPUT("PWM3"),
+ SND_SOC_DAPM_OUTPUT("PWM4"),
+ SND_SOC_DAPM_OUTPUT("PWM5"),
+ SND_SOC_DAPM_OUTPUT("PWM6"),
+
+ SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[0]),
+ SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[1]),
+ SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[2]),
+ SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[3]),
+ SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[4]),
+ SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[5]),
+
+ SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[0]),
+ SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[1]),
+ SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[2]),
+ SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[3]),
+ SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[4]),
+ SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[5]),
+};
+
+static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
+ /* SDIN inputs -> channel muxes */
+ { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" },
+
+ /* Channel muxes -> PWM muxes */
+ { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+
+ { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+
+ { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+
+ { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+
+ { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+
+ { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+
+ /* The PWM muxes are directly connected to the PWM outputs */
+ { "PWM1", NULL, "PWM1 Mux" },
+ { "PWM2", NULL, "PWM2 Mux" },
+ { "PWM3", NULL, "PWM3 Mux" },
+ { "PWM4", NULL, "PWM4 Mux" },
+ { "PWM5", NULL, "PWM5 Mux" },
+ { "PWM6", NULL, "PWM6 Mux" },
+
+};
+
static const struct snd_soc_dai_ops tas5086_dai_ops = {
.hw_params = tas5086_hw_params,
.set_sysclk = tas5086_set_dai_sysclk,
@@ -426,13 +721,34 @@ static int tas5086_probe(struct snd_soc_codec *codec)
{
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int charge_period = 1300000; /* hardware default is 1300 ms */
+ u8 pwm_start_mid_z = 0;
int i, ret;
if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
struct device_node *of_node = codec->dev->of_node;
of_property_read_u32(of_node, "ti,charge-period", &charge_period);
+
+ for (i = 0; i < 6; i++) {
+ char name[25];
+
+ snprintf(name, sizeof(name),
+ "ti,mid-z-channel-%d", i + 1);
+
+ if (of_get_property(of_node, name, NULL) != NULL)
+ pwm_start_mid_z |= 1 << i;
+ }
}
+ /*
+ * If any of the channels is configured to start in Mid-Z mode,
+ * configure 'part 1' of the PWM starts to use Mid-Z, and tell
+ * all configured mid-z channels to start start under 'part 1'.
+ */
+ if (pwm_start_mid_z)
+ regmap_write(priv->regmap, TAS5086_PWM_START,
+ TAS5086_PWM_START_MIDZ_FOR_START_1 |
+ pwm_start_mid_z);
+
/* lookup and set split-capacitor charge period */
if (charge_period == 0) {
regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
@@ -490,6 +806,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.resume = tas5086_soc_resume,
.controls = tas5086_controls,
.num_controls = ARRAY_SIZE(tas5086_controls),
+ .dapm_widgets = tas5086_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
+ .dapm_routes = tas5086_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
};
static const struct i2c_device_id tas5086_i2c_id[] = {
@@ -500,14 +820,16 @@ MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
static const struct regmap_config tas5086_regmap = {
.reg_bits = 8,
- .val_bits = 8,
- .max_register = ARRAY_SIZE(tas5086_reg_defaults),
+ .val_bits = 32,
+ .max_register = TAS5086_MAX_REGISTER,
.reg_defaults = tas5086_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = tas5086_volatile_reg,
.writeable_reg = tas5086_writeable_reg,
.readable_reg = tas5086_accessible_reg,
+ .reg_read = tas5086_reg_read,
+ .reg_write = tas5086_reg_write,
};
static int tas5086_i2c_probe(struct i2c_client *i2c,
@@ -522,7 +844,7 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
- priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap);
+ priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 1514bf845e4b..e5b926883131 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -128,10 +128,8 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
};
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
+ snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)
/*
* All input lines are connected when !0xf and disconnected with 0xf bit field,
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 9b9a6e587610..44621ddc332d 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -38,6 +38,14 @@
#include "twl6040.h"
+enum twl6040_dai_id {
+ TWL6040_DAI_LEGACY = 0,
+ TWL6040_DAI_UL,
+ TWL6040_DAI_DL1,
+ TWL6040_DAI_DL2,
+ TWL6040_DAI_VIB,
+};
+
#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
@@ -67,6 +75,8 @@ struct twl6040_data {
int pll_power_mode;
int hs_power_mode;
int hs_power_mode_locked;
+ bool dl1_unmuted;
+ bool dl2_unmuted;
unsigned int clk_in;
unsigned int sysclk;
struct twl6040_jack_data hs_jack;
@@ -220,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
return value;
}
+static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ /* DL1 path */
+ return priv->dl1_unmuted;
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ return priv->dl2_unmuted;
+ default:
+ return 1;
+ };
+}
+
/*
* write to the twl6040 register space
*/
@@ -232,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
return -EIO;
twl6040_write_reg_cache(codec, reg, value);
- if (likely(reg < TWL6040_REG_SW_SHADOW))
+ if (likely(reg < TWL6040_REG_SW_SHADOW) &&
+ twl6040_is_path_unmuted(codec, reg))
return twl6040_reg_write(twl6040, reg, value);
else
return 0;
@@ -1026,16 +1056,84 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
+ int mute)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int hslctl, hsrctl, earctl;
+ int hflctl, hfrctl;
+
+ switch (id) {
+ case TWL6040_DAI_DL1:
+ hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
+ earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ earctl &= ~0x01;
+ hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+ hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
+ priv->dl1_unmuted = !mute;
+ break;
+ case TWL6040_DAI_DL2:
+ hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
+ hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
+ priv->dl2_unmuted = !mute;
+ break;
+ default:
+ break;
+ };
+}
+
+static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ switch (dai->id) {
+ case TWL6040_DAI_LEGACY:
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
+ break;
+ case TWL6040_DAI_DL1:
+ case TWL6040_DAI_DL2:
+ twl6040_mute_path(dai->codec, dai->id, mute);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops twl6040_dai_ops = {
.startup = twl6040_startup,
.hw_params = twl6040_hw_params,
.prepare = twl6040_prepare,
.set_sysclk = twl6040_set_dai_sysclk,
+ .digital_mute = twl6040_digital_mute,
};
static struct snd_soc_dai_driver twl6040_dai[] = {
{
.name = "twl6040-legacy",
+ .id = TWL6040_DAI_LEGACY,
.playback = {
.stream_name = "Legacy Playback",
.channels_min = 1,
@@ -1054,6 +1152,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-ul",
+ .id = TWL6040_DAI_UL,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -1065,6 +1164,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-dl1",
+ .id = TWL6040_DAI_DL1,
.playback = {
.stream_name = "Headset Playback",
.channels_min = 1,
@@ -1076,6 +1176,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-dl2",
+ .id = TWL6040_DAI_DL2,
.playback = {
.stream_name = "Handsfree Playback",
.channels_min = 1,
@@ -1087,6 +1188,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-vib",
+ .id = TWL6040_DAI_VIB,
.playback = {
.stream_name = "Vibra Playback",
.channels_min = 1,
@@ -1143,7 +1245,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
mutex_init(&priv->mutex);
- ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL,
+ ret = request_threaded_irq(priv->plug_irq, NULL,
twl6040_audio_handler, IRQF_NO_SUSPEND,
"twl6040_irq_plug", codec);
if (ret) {
@@ -1159,6 +1261,9 @@ static int twl6040_probe(struct snd_soc_codec *codec)
static int twl6040_remove(struct snd_soc_codec *codec)
{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ free_irq(priv->plug_irq, codec);
twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 370af0cbcc9a..f5e835662cdc 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
@@ -972,6 +973,13 @@ static int wm0010_spi_probe(struct spi_device *spi)
}
wm0010->irq = irq;
+ ret = irq_set_irq_wake(irq, 1);
+ if (ret) {
+ dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n",
+ irq, ret);
+ return ret;
+ }
+
if (spi->max_speed_hz)
wm0010->board_max_spi_speed = spi->max_speed_hz;
else
@@ -995,6 +1003,8 @@ static int wm0010_spi_remove(struct spi_device *spi)
gpio_set_value_cansleep(wm0010->gpio_reset,
wm0010->gpio_reset_value);
+ irq_set_irq_wake(wm0010->irq, 0);
+
if (wm0010->irq)
free_irq(wm0010->irq, wm0010);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 100fdadda56a..282fd232cdf7 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -814,7 +814,20 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
-SOC_VALUE_ENUM("HPOUT3 OSR", wm5102_hpout_osr[2]),
+SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
+
+SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
+SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE3L_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2,
+ ARIZONA_DRE_T_LOW_SHIFT, 63, 0),
+
+SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3,
+ ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -852,6 +865,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -898,6 +920,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@@ -1117,6 +1148,56 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
@@ -1189,6 +1270,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@@ -1249,6 +1339,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@@ -1304,10 +1402,21 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "OUT5L", NULL, "SYSCLK" },
{ "OUT5R", NULL, "SYSCLK" },
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
@@ -1345,13 +1454,41 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1408,6 +1545,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@@ -1560,6 +1706,63 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
+ {
+ .name = "wm5102-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
};
static int wm5102_codec_probe(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 88ad7db52dde..2e7cb4ba161a 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -309,6 +309,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -360,6 +369,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@@ -550,6 +568,56 @@ SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
@@ -640,6 +708,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@@ -690,6 +767,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@@ -736,10 +821,23 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT6L", NULL, "SYSCLK" },
{ "OUT6R", NULL, "SYSCLK" },
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
@@ -777,13 +875,41 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -829,6 +955,15 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@@ -963,6 +1098,63 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
+ {
+ .name = "wm5110-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
};
static int wm5110_codec_probe(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index af6d227e67be..d2a092850283 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -143,13 +143,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array)
static const char *wm8400_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 9d88437cdcd1..fa24cedee687 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -403,10 +403,8 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
}
#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8903_class_w_put)
static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 3ff195c541db..4c9fb142cb2d 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -603,13 +603,8 @@ SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
SOC_ENUM("High Pass Filter Mode", hpf_mode),
-
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "ADC 128x OSR Switch",
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,
- .put = wm8904_adc_osr_put,
- .private_value = SOC_SINGLE_VALUE(WM8904_ANALOGUE_ADC_0, 0, 1, 0),
-},
+SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0,
+ snd_soc_get_volsw, wm8904_adc_osr_put),
};
static const char *drc_path_text[] = {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e9710280e5e1..b1dc7d426438 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -51,6 +51,7 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = {
/* codec private data */
struct wm8962_priv {
+ struct wm8962_pdata pdata;
struct regmap *regmap;
struct snd_soc_codec *codec;
@@ -1600,7 +1601,6 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- u16 *reg_cache = codec->reg_cache;
int ret;
/* Apply the update (if any) */
@@ -1609,16 +1609,19 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
return 0;
/* If the left PGA is enabled hit that VU bit... */
- if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTL_PGA_ENA)
- return snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
- reg_cache[WM8962_HPOUTL_VOLUME]);
+ ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ if (ret & WM8962_HPOUTL_PGA_ENA) {
+ snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTL_VOLUME));
+ return 1;
+ }
/* ...otherwise the right. The VU is stereo. */
- if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTR_PGA_ENA)
- return snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
- reg_cache[WM8962_HPOUTR_VOLUME]);
+ if (ret & WM8962_HPOUTR_PGA_ENA)
+ snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTR_VOLUME));
- return 0;
+ return 1;
}
/* The VU bits for the speakers are in a different register to the mute
@@ -2345,12 +2348,13 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
static int wm8962_add_widgets(struct snd_soc_codec *codec)
{
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_add_codec_controls(codec, wm8962_snd_controls,
ARRAY_SIZE(wm8962_snd_controls));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls,
ARRAY_SIZE(wm8962_spk_mono_controls));
else
@@ -2360,7 +2364,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets,
ARRAY_SIZE(wm8962_dapm_widgets));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets,
ARRAY_SIZE(wm8962_dapm_spk_mono_widgets));
else
@@ -2369,7 +2373,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, wm8962_intercon,
ARRAY_SIZE(wm8962_intercon));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon,
ARRAY_SIZE(wm8962_spk_mono_intercon));
else
@@ -3333,14 +3337,14 @@ static struct gpio_chip wm8962_template_chip = {
static void wm8962_init_gpio(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
int ret;
wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
wm8962->gpio_chip.dev = codec->dev;
- if (pdata && pdata->gpio_base)
+ if (pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
else
wm8962->gpio_chip.base = -1;
@@ -3374,7 +3378,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
- u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@@ -3421,30 +3424,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
0);
- if (pdata) {
- /* Apply static configuration for GPIOs */
- for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
- if (pdata->gpio_init[i]) {
- wm8962_set_gpio_mode(codec, i + 1);
- snd_soc_write(codec, 0x200 + i,
- pdata->gpio_init[i] & 0xffff);
- }
+ /* Apply static configuration for GPIOs */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
+ if (pdata->gpio_init[i]) {
+ wm8962_set_gpio_mode(codec, i + 1);
+ snd_soc_write(codec, 0x200 + i,
+ pdata->gpio_init[i] & 0xffff);
+ }
- /* Put the speakers into mono mode? */
- if (pdata->spk_mono)
- reg_cache[WM8962_CLASS_D_CONTROL_2]
- |= WM8962_SPK_MONO;
- /* Micbias setup, detection enable and detection
- * threasholds. */
- if (pdata->mic_cfg)
- snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
- WM8962_MICDET_ENA |
- WM8962_MICDET_THR_MASK |
- WM8962_MICSHORT_THR_MASK |
- WM8962_MICBIAS_LVL,
- pdata->mic_cfg);
- }
+ /* Put the speakers into mono mode? */
+ if (pdata->spk_mono)
+ snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_2,
+ WM8962_SPK_MONO_MASK, WM8962_SPK_MONO);
+
+ /* Micbias setup, detection enable and detection
+ * threasholds. */
+ if (pdata->mic_cfg)
+ snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_MICDET_ENA |
+ WM8962_MICDET_THR_MASK |
+ WM8962_MICSHORT_THR_MASK |
+ WM8962_MICBIAS_LVL,
+ pdata->mic_cfg);
/* Latch volume update bits */
snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
@@ -3506,7 +3508,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_gpio(codec);
if (wm8962->irq) {
- if (pdata && pdata->irq_active_low) {
+ if (pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
} else {
@@ -3584,6 +3586,34 @@ static const struct regmap_config wm8962_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8962_pdata *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ u32 val32;
+ int i;
+
+ if (of_property_read_bool(np, "spk-mono"))
+ pdata->spk_mono = true;
+
+ if (of_property_read_u32(np, "mic-cfg", &val32) >= 0)
+ pdata->mic_cfg = val32;
+
+ if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init,
+ ARRAY_SIZE(pdata->gpio_init)) >= 0)
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) {
+ /*
+ * The range of GPIO register value is [0x0, 0xffff]
+ * While the default value of each register is 0x0
+ * Any other value will be regarded as default value
+ */
+ if (pdata->gpio_init[i] > 0xffff)
+ pdata->gpio_init[i] = 0x0;
+ }
+
+ return 0;
+}
+
static int wm8962_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -3603,6 +3633,15 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
init_completion(&wm8962->fll_lock);
wm8962->irq = i2c->irq;
+ /* If platform data was supplied, update the default data in priv */
+ if (pdata) {
+ memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata));
+ } else if (i2c->dev.of_node) {
+ ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata);
+ if (ret != 0)
+ return ret;
+ }
+
for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
wm8962->supplies[i].supply = wm8962_supply_names[i];
@@ -3666,7 +3705,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
- if (pdata && pdata->in4_dc_measure) {
+ if (wm8962->pdata.in4_dc_measure) {
ret = regmap_register_patch(wm8962->regmap,
wm8962_dc_measure,
ARRAY_SIZE(wm8962_dc_measure));
@@ -3719,8 +3758,34 @@ static int wm8962_runtime_resume(struct device *dev)
wm8962_reset(wm8962);
+ /* SYSCLK defaults to on; make sure it is off so we can safely
+ * write to registers if the device is declocked.
+ */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA, 0);
+
+ /* Ensure we have soft control over all registers */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
+
+ /* Ensure that the oscillator and PLLs are disabled */
+ regmap_update_bits(wm8962->regmap, WM8962_PLL2,
+ WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
+ 0);
+
regcache_sync(wm8962->regmap);
+ regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
+
+ /* Bias enable at 2*5k (fast start-up) */
+ regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
+ WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK,
+ WM8962_BIAS_ENA | 0x180);
+
+ msleep(5);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 837978e16e9d..253c88bb7a4c 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -151,14 +151,9 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
- tlv_array) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
static const char *wm8990_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 8a942efd18a5..07707d8d7e20 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -822,12 +822,7 @@
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
#endif /* _WM8991_H */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 29e95f93d482..1d4b1ec66e36 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -289,10 +290,8 @@ static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 0);
#define WM8994_DRC_SWITCH(xname, reg, shift) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
- .put = wm8994_put_drc_sw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, 1, 0) }
+ SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \
+ snd_soc_get_volsw, wm8994_put_drc_sw)
static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1432,10 +1431,8 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
};
#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8994_put_class_w, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8994_put_class_w)
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1498,6 +1495,24 @@ static const char *aif1dac_text[] = {
"AIF1DACDAT", "AIF3DACDAT",
};
+static const char *loopback_text[] = {
+ "None", "ADCDAT",
+};
+
+static const struct soc_enum aif1_loopback_enum =
+ SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif1_loopback =
+ SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum);
+
+static const struct soc_enum aif2_loopback_enum =
+ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif2_loopback =
+ SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum);
+
static const struct soc_enum aif1dac_enum =
SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
@@ -1744,6 +1759,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback),
+SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback),
+
SND_SOC_DAPM_POST("Debug log", post_ev),
};
@@ -1875,9 +1893,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
- { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
+ { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" },
{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
- { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
+ { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" },
{ "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
@@ -1928,6 +1946,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+ /* Loopback */
+ { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
+ { "AIF1 Loopback", "None", "AIF1DACDAT" },
+ { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" },
+ { "AIF2 Loopback", "None", "AIF2DACDAT" },
+
/* Sidetone */
{ "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
{ "Left Sidetone", "DMIC2", "DMIC2L" },
@@ -2010,15 +2034,16 @@ struct fll_div {
u16 outdiv;
u16 n;
u16 k;
+ u16 lambda;
u16 clk_ref_div;
u16 fll_fratio;
};
-static int wm8994_get_fll_config(struct fll_div *fll,
+static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
int freq_in, int freq_out)
{
u64 Kpart;
- unsigned int K, Ndiv, Nmod;
+ unsigned int K, Ndiv, Nmod, gcd_fll;
pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
@@ -2067,20 +2092,32 @@ static int wm8994_get_fll_config(struct fll_div *fll,
Nmod = freq_out % freq_in;
pr_debug("Nmod=%d\n", Nmod);
- /* Calculate fractional part - scale up so we can round. */
- Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+ switch (control->type) {
+ case WM8994:
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, freq_in);
+
+ K = Kpart & 0xFFFFFFFF;
- do_div(Kpart, freq_in);
+ if ((K % 10) >= 5)
+ K += 5;
- K = Kpart & 0xFFFFFFFF;
+ /* Move down to proper range now rounding is done */
+ fll->k = K / 10;
+ fll->lambda = 0;
- if ((K % 10) >= 5)
- K += 5;
+ pr_debug("N=%x K=%x\n", fll->n, fll->k);
+ break;
- /* Move down to proper range now rounding is done */
- fll->k = K / 10;
+ default:
+ gcd_fll = gcd(freq_out, freq_in);
- pr_debug("N=%x K=%x\n", fll->n, fll->k);
+ fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
+ fll->lambda = freq_in / gcd_fll;
+
+ }
return 0;
}
@@ -2144,9 +2181,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
* analysis bugs spewing warnings.
*/
if (freq_out)
- ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
+ ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
else
- ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
+ ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
wm8994->fll[id].out);
if (ret < 0)
return ret;
@@ -2191,6 +2228,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_N_MASK,
fll.n << WM8994_FLL1_N_SHIFT);
+ if (fll.lambda) {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
+ WM8958_FLL1_LAMBDA_MASK,
+ fll.lambda);
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, 0);
+ }
+
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
@@ -2555,17 +2603,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct wm8994 *control = wm8994->wm8994;
int ms_reg;
int aif1_reg;
+ int dac_reg;
+ int adc_reg;
int ms = 0;
int aif1 = 0;
+ int lrclk = 0;
switch (dai->id) {
case 1:
ms_reg = WM8994_AIF1_MASTER_SLAVE;
aif1_reg = WM8994_AIF1_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
break;
case 2:
ms_reg = WM8994_AIF2_MASTER_SLAVE;
aif1_reg = WM8994_AIF2_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
break;
default:
return -EINVAL;
@@ -2584,6 +2639,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif1 |= WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x18;
break;
@@ -2622,12 +2678,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_IB_IF:
aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
aif1 |= WM8994_AIF1_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF:
aif1 |= WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
break;
default:
return -EINVAL;
@@ -2658,6 +2716,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
aif1);
snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
ms);
+ snd_soc_update_bits(codec, dac_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
+ snd_soc_update_bits(codec, adc_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
return 0;
}
@@ -3096,24 +3158,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
static int wm8994_codec_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994 *control = wm8994->wm8994;
int i, ret;
- unsigned int val, mask;
-
- if (control->revision < 4) {
- /* force a HW read */
- ret = regmap_read(control->regmap,
- WM8994_POWER_MANAGEMENT_5, &val);
-
- /* modify the cache only */
- codec->cache_only = 1;
- mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA |
- WM8994_DAC2R_ENA | WM8994_DAC2L_ENA;
- val &= mask;
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
- mask, val);
- codec->cache_only = 0;
- }
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
if (!wm8994->fll_suspend[i].out)
@@ -3495,6 +3540,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
wm8994->btn_mask);
}
+static void wm8958_open_circuit_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ open_circuit_work.work);
+ struct device *dev = wm8994->wm8994->dev;
+
+ wm1811_micd_stop(wm8994->hubs.codec);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ dev_dbg(dev, "Reporting open circuit\n");
+
+ wm8994->jack_mic = false;
+ wm8994->mic_detecting = true;
+
+ wm8958_micd_set_rate(wm8994->hubs.codec);
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+ wm8994->btn_mask |
+ SND_JACK_HEADSET);
+
+ mutex_unlock(&wm8994->accdet_lock);
+}
+
static void wm8958_mic_id(void *data, u16 status)
{
struct snd_soc_codec *codec = data;
@@ -3504,16 +3574,9 @@ static void wm8958_mic_id(void *data, u16 status)
if (!(status & WM8958_MICD_STS)) {
/* If nothing present then clear our statuses */
dev_dbg(codec->dev, "Detected open circuit\n");
- wm8994->jack_mic = false;
- wm8994->mic_detecting = true;
-
- wm1811_micd_stop(codec);
-
- wm8958_micd_set_rate(codec);
- snd_soc_jack_report(wm8994->micdet[0].jack, 0,
- wm8994->btn_mask |
- SND_JACK_HEADSET);
+ schedule_delayed_work(&wm8994->open_circuit_work,
+ msecs_to_jiffies(2500));
return;
}
@@ -3598,6 +3661,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
pm_runtime_get_sync(codec->dev);
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+
mutex_lock(&wm8994->accdet_lock);
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
@@ -3780,11 +3845,33 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
EXPORT_SYMBOL_GPL(wm8958_mic_detect);
+static void wm8958_mic_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ mic_complete_work.work);
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+
+ dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status);
+
+ pm_runtime_get_sync(codec->dev);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status);
+
+ mutex_unlock(&wm8994->accdet_lock);
+
+ pm_runtime_put(codec->dev);
+
+ dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status);
+}
+
static irqreturn_t wm8958_mic_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
struct snd_soc_codec *codec = wm8994->hubs.codec;
- int reg, count, ret;
+ int reg, count, ret, id_delay;
/*
* Jack detection may have detected a removal simulataneously
@@ -3794,6 +3881,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
+
pm_runtime_get_sync(codec->dev);
/* We may occasionally read a detection without an impedence
@@ -3846,8 +3936,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
goto out;
}
+ wm8994->mic_status = reg;
+ id_delay = wm8994->wm8994->pdata.mic_id_delay;
+
if (wm8994->mic_detecting)
- wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg);
+ schedule_delayed_work(&wm8994->mic_complete_work,
+ msecs_to_jiffies(id_delay));
else
wm8958_button_det(codec, reg);
@@ -3899,6 +3993,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
mutex_init(&wm8994->accdet_lock);
INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
wm1811_jackdet_bootstrap);
+ INIT_DELAYED_WORK(&wm8994->open_circuit_work,
+ wm8958_open_circuit_work);
switch (control->type) {
case WM8994:
@@ -3911,6 +4007,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
+ INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
+
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 55ddf4d57d9b..6536f8d45ac6 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -134,6 +134,9 @@ struct wm8994_priv {
struct mutex accdet_lock;
struct wm8994_micdet micdet[2];
struct delayed_work mic_work;
+ struct delayed_work open_circuit_work;
+ struct delayed_work mic_complete_work;
+ u16 mic_status;
bool mic_detecting;
bool jack_mic;
int btn_mask;
diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h
index 5642121c4977..508ad27fe2bb 100644
--- a/sound/soc/codecs/wm8995.h
+++ b/sound/soc/codecs/wm8995.h
@@ -4237,11 +4237,8 @@
#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */
#define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8995_put_class_w, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) \
-}
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8995_put_class_w)
struct wm8995_reg_access {
u16 read;
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 05b1f346695b..70ce6793c5bd 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -209,7 +209,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -225,7 +225,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
@@ -294,8 +294,8 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
static int wm9705_reset(struct snd_soc_codec *codec)
{
- if (soc_ac97_ops.reset) {
- soc_ac97_ops.reset(codec->ac97);
+ if (soc_ac97_ops->reset) {
+ soc_ac97_ops->reset(codec->ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
@@ -306,7 +306,7 @@ static int wm9705_reset(struct snd_soc_codec *codec)
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
- soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
+ soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
@@ -323,7 +323,7 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
}
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
return 0;
@@ -337,9 +337,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "WM9705 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 8e9a6a3eeb1a..c5eb746087b4 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -455,7 +455,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -472,7 +472,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
@@ -581,15 +581,15 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -624,7 +624,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
(i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -635,7 +635,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index f7afa68d8c7f..a53e175c015a 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -652,7 +652,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -668,7 +668,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
@@ -1095,15 +1095,15 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
@@ -1180,7 +1180,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -1197,7 +1197,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3470b649c0b2..05252ac936a3 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -215,6 +216,36 @@ static struct {
[WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
+struct wm_coeff_ctl_ops {
+ int (*xget)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xput)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xinfo)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+};
+
+struct wm_coeff {
+ struct device *dev;
+ struct list_head ctl_list;
+ struct regmap *regmap;
+};
+
+struct wm_coeff_ctl {
+ const char *name;
+ struct snd_card *card;
+ struct wm_adsp_alg_region region;
+ struct wm_coeff_ctl_ops ops;
+ struct wm_adsp *adsp;
+ void *private;
+ unsigned int enabled:1;
+ struct list_head list;
+ void *cache;
+ size_t len;
+ unsigned int set:1;
+ struct snd_kcontrol *kcontrol;
+};
+
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -279,7 +310,7 @@ static const struct soc_enum wm_adsp2_rate_enum[] = {
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
@@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
}
}
+static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ return 0;
+}
+
+static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+ const void *buf, size_t len)
+{
+ struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
+ ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to write %zu bytes to %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(ctl->cache, p, ctl->len);
+
+ if (!ctl->enabled) {
+ ctl->set = 1;
+ return 0;
+ }
+
+ return wm_coeff_write_control(kcontrol, p, ctl->len);
+}
+
+static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+ void *buf, size_t len)
+{
+ struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to read %zu bytes from %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ memcpy(buf, scratch, ctl->len);
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(p, ctl->cache, ctl->len);
+ return 0;
+}
+
+static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
+ struct wm_coeff_ctl *ctl,
+ const struct snd_kcontrol_new *kctl)
+{
+ int ret;
+ struct snd_kcontrol *kcontrol;
+
+ kcontrol = snd_ctl_new1(kctl, wm_coeff);
+ ret = snd_ctl_add(ctl->card, kcontrol);
+ if (ret < 0) {
+ dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
+ kctl->name, ret);
+ return ret;
+ }
+ ctl->kcontrol = kcontrol;
+ return 0;
+}
+
+struct wmfw_ctl_work {
+ struct wm_coeff *wm_coeff;
+ struct wm_coeff_ctl *ctl;
+ struct work_struct work;
+};
+
+static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
+ struct wm_coeff_ctl *ctl)
+{
+ struct snd_kcontrol_new *kcontrol;
+ int ret;
+
+ if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
+ return -EINVAL;
+
+ kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+
+ kcontrol->name = ctl->name;
+ kcontrol->info = wm_coeff_info;
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
+ kcontrol->private_value = (unsigned long)ctl;
+
+ ret = wm_coeff_add_kcontrol(wm_coeff,
+ ctl, kcontrol);
+ if (ret < 0)
+ goto err_kcontrol;
+
+ kfree(kcontrol);
+
+ list_add(&ctl->list, &wm_coeff->ctl_list);
+ return 0;
+
+err_kcontrol:
+ kfree(kcontrol);
+ return ret;
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -547,7 +753,157 @@ out:
return ret;
}
-static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &wm_coeff->ctl_list,
+ list) {
+ if (!ctl->enabled || ctl->set)
+ continue;
+ ret = wm_coeff_read_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &wm_coeff->ctl_list,
+ list) {
+ if (!ctl->enabled)
+ continue;
+ if (ctl->set) {
+ ret = wm_coeff_write_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+ struct wmfw_ctl_work *ctl_work = container_of(work,
+ struct wmfw_ctl_work,
+ work);
+
+ wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
+ kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct snd_soc_codec *codec,
+ const struct wm_adsp_alg_region *region)
+
+{
+ struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+ struct wm_coeff_ctl *ctl;
+ struct wmfw_ctl_work *ctl_work;
+ char *name;
+ char *region_name;
+ int ret;
+
+ name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_name;
+ }
+
+ snprintf(name, PAGE_SIZE, "DSP%d %s %x",
+ dsp->num, region_name, region->alg);
+
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ if (!strcmp(ctl->name, name)) {
+ if (!ctl->enabled)
+ ctl->enabled = 1;
+ goto found;
+ }
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl) {
+ ret = -ENOMEM;
+ goto err_name;
+ }
+ ctl->region = *region;
+ ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+ if (!ctl->name) {
+ ret = -ENOMEM;
+ goto err_ctl;
+ }
+ ctl->enabled = 1;
+ ctl->set = 0;
+ ctl->ops.xget = wm_coeff_get;
+ ctl->ops.xput = wm_coeff_put;
+ ctl->card = codec->card->snd_card;
+ ctl->adsp = dsp;
+
+ ctl->len = region->len;
+ ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ if (!ctl->cache) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+
+ ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+ if (!ctl_work) {
+ ret = -ENOMEM;
+ goto err_ctl_cache;
+ }
+
+ ctl_work->wm_coeff = dsp->wm_coeff;
+ ctl_work->ctl = ctl;
+ INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+ schedule_work(&ctl_work->work);
+
+found:
+ kfree(name);
+
+ return 0;
+
+err_ctl_cache:
+ kfree(ctl->cache);
+err_ctl_name:
+ kfree(ctl->name);
+err_ctl:
+ kfree(ctl);
+err_name:
+ kfree(name);
+ return ret;
+}
+
+static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id;
@@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].dm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
+ region->len -= be32_to_cpu(adsp1_alg[i].dm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp1_alg[i].zm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
break;
case WMFW_ADSP2:
@@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].xm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
+ region->len -= be32_to_cpu(adsp2_alg[i].xm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].ym);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
+ region->len -= be32_to_cpu(adsp2_alg[i].ym);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp2_alg[i].zm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
break;
}
}
@@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = w->codec;
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
+ struct wm_coeff_ctl *ctl;
int ret;
int val;
@@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START,
@@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
+
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ ctl->enabled = 0;
+ }
break;
default:
@@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
+ struct wm_coeff_ctl *ctl;
unsigned int val;
int ret;
@@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret);
}
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ ctl->enabled = 0;
+ }
+
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
@@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
INIT_LIST_HEAD(&adsp->alg_regions);
+ adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
+ GFP_KERNEL);
+ if (!adsp->wm_coeff)
+ return -ENOMEM;
+ adsp->wm_coeff->regmap = adsp->regmap;
+ adsp->wm_coeff->dev = adsp->dev;
+ INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
+
if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) {
ret = PTR_ERR(adsp->dvfs);
dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
- return ret;
+ goto out_coeff;
}
ret = regulator_enable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
if (ret != 0) {
dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
ret = regulator_disable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
}
return 0;
+
+out_coeff:
+ kfree(adsp->wm_coeff);
+ return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index fea514627526..9f922c82536c 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
unsigned int alg;
int type;
unsigned int base;
+ size_t len;
};
struct wm_adsp {
@@ -55,17 +56,17 @@ struct wm_adsp {
bool running;
struct regulator *dvfs;
+
+ struct wm_coeff *wm_coeff;
};
#define WM_ADSP1(wname, num) \
- { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = num, .event = wm_adsp1_event, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
#define WM_ADSP2(wname, num) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = num, .event = wm_adsp2_event, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f5d81b948759..2d9e099415a5 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -693,10 +693,8 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, class_w_put_volsw)
static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 9e11a14d1b45..c82f89c9475b 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -54,16 +54,6 @@ config SND_DM6467_SOC_EVM
help
Say Y if you want to add support for SoC audio on TI
-config SND_DAVINCI_SOC_SFFSDR
- tristate "SoC Audio support for SFFSDR"
- depends on SND_DAVINCI_SOC && MACH_SFFSDR
- select SND_DAVINCI_SOC_I2S
- select SND_SOC_PCM3008
- select SFFSDR_FPGA
- help
- Say Y if you want to add support for SoC audio on
- Lyrtech SFFSDR board.
-
config SND_DA830_SOC_EVM
tristate "SoC Audio support for DA830/OMAP-L137 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index a93679d618cd..a396ab6d6d5e 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -11,10 +11,8 @@ obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
# DAVINCI Machine Support
snd-soc-evm-objs := davinci-evm.o
-snd-soc-sffsdr-objs := davinci-sffsdr.o
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 484b22c5df5d..fd7c45b9ed5a 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -14,6 +14,7 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 81490febac6d..32ddb7fe5034 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -1024,7 +1024,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
struct device_node *np = pdev->dev.of_node;
struct snd_platform_data *pdata = NULL;
const struct of_device_id *match =
- of_match_device(of_match_ptr(mcasp_dt_ids), &pdev->dev);
+ of_match_device(mcasp_dt_ids, &pdev->dev);
const u32 *of_serial_dir32;
u8 *of_serial_dir;
@@ -1257,7 +1257,7 @@ static struct platform_driver davinci_mcasp_driver = {
.driver = {
.name = "davinci-mcasp",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(mcasp_dt_ids),
+ .of_match_table = mcasp_dt_ids,
},
};
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index b2f27c2e5fdc..8460edce1c3b 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -17,6 +17,7 @@
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/genalloc.h>
+#include <linux/platform_data/edma.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index b6ef7039dd09..fbb710c76c08 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -14,7 +14,7 @@
#include <linux/genalloc.h>
#include <linux/platform_data/davinci_asp.h>
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
struct davinci_pcm_dma_params {
int channel; /* sync dma channel ID */
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
deleted file mode 100644
index 5be65aae7e0e..000000000000
--- a/sound/soc/davinci/davinci-sffsdr.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * ASoC driver for Lyrtech SFFSDR board.
- *
- * Author: Hugo Villeneuve
- * Copyright (C) 2008 Lyrtech inc
- *
- * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <asm/mach-types.h>
-#ifdef CONFIG_SFFSDR_FPGA
-#include <asm/plat-sffsdr/sffsdr-fpga.h>
-#endif
-
-#include <mach/edma.h>
-
-#include "../codecs/pcm3008.h"
-#include "davinci-pcm.h"
-#include "davinci-i2s.h"
-
-/*
- * CLKX and CLKR are the inputs for the Sample Rate Generator.
- * FSX and FSR are outputs, driven by the sample Rate Generator.
- */
-#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
- SND_SOC_DAIFMT_CBM_CFS | \
- SND_SOC_DAIFMT_IB_NF)
-
-static int sffsdr_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int fs;
- int ret = 0;
-
- /* Fsref can be 32000, 44100 or 48000. */
- fs = params_rate(params);
-
-#ifndef CONFIG_SFFSDR_FPGA
- /* Without the FPGA module, the Fs is fixed at 44100 Hz */
- if (fs != 44100) {
- pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n");
- return -EINVAL;
- }
-#endif
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
-
- pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
-
-#ifndef CONFIG_SFFSDR_FPGA
- return 0;
-#else
- return sffsdr_fpga_set_codec_fs(fs);
-#endif
-}
-
-static struct snd_soc_ops sffsdr_ops = {
- .hw_params = sffsdr_hw_params,
-};
-
-/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sffsdr_dai = {
- .name = "PCM3008", /* Codec name */
- .stream_name = "PCM3008 HiFi",
- .cpu_dai_name = "davinci-mcbsp",
- .codec_dai_name = "pcm3008-hifi",
- .codec_name = "pcm3008-codec",
- .platform_name = "davinci-mcbsp",
- .ops = &sffsdr_ops,
-};
-
-/* davinci-sffsdr audio machine driver */
-static struct snd_soc_card snd_soc_sffsdr = {
- .name = "DaVinci SFFSDR",
- .owner = THIS_MODULE,
- .dai_link = &sffsdr_dai,
- .num_links = 1,
-};
-
-/* sffsdr audio private data */
-static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
- .dem0_pin = GPIO(45),
- .dem1_pin = GPIO(46),
- .pdad_pin = GPIO(47),
- .pdda_pin = GPIO(38),
-};
-
-struct platform_device pcm3008_codec = {
- .name = "pcm3008-codec",
- .id = 0,
- .dev = {
- .platform_data = &sffsdr_pcm3008_setup,
- },
-};
-
-static struct resource sffsdr_snd_resources[] = {
- {
- .start = DAVINCI_MCBSP_BASE,
- .end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct evm_snd_platform_data sffsdr_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
- .rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
-};
-
-static struct platform_device *sffsdr_snd_device;
-
-static int __init sffsdr_init(void)
-{
- int ret;
-
- if (!machine_is_sffsdr())
- return -EINVAL;
-
- platform_device_register(&pcm3008_codec);
-
- sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
- if (!sffsdr_snd_device) {
- printk(KERN_ERR "platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(sffsdr_snd_device, &snd_soc_sffsdr);
- platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data,
- sizeof(sffsdr_snd_data));
-
- ret = platform_device_add_resources(sffsdr_snd_device,
- sffsdr_snd_resources,
- ARRAY_SIZE(sffsdr_snd_resources));
- if (ret) {
- printk(KERN_ERR "platform device add resources failed\n");
- goto error;
- }
-
- ret = platform_device_add(sffsdr_snd_device);
- if (ret)
- goto error;
-
- return ret;
-
-error:
- platform_device_put(sffsdr_snd_device);
- return ret;
-}
-
-static void __exit sffsdr_exit(void)
-{
- platform_device_unregister(sffsdr_snd_device);
- platform_device_unregister(&pcm3008_codec);
-}
-
-module_init(sffsdr_init);
-module_exit(sffsdr_exit);
-
-MODULE_AUTHOR("Hugo Villeneuve");
-MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 593a3ea12d4c..70eb37a5dd16 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -1,7 +1,7 @@
/*
* ALSA SoC Synopsys I2S Audio Layer
*
- * sound/soc/spear/designware_i2s.c
+ * sound/soc/dwc/designware_i2s.c
*
* Copyright (C) 2010 ST Microelectronics
* Rajeev Kumar <rajeev-dlh.kumar@st.com>
@@ -396,7 +396,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
}
if (cap & DWC_I2S_PLAY) {
- dev_dbg(&pdev->dev, " SPEAr: play supported\n");
+ dev_dbg(&pdev->dev, " designware: play supported\n");
dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
dw_i2s_dai->playback.channels_max = pdata->channel;
dw_i2s_dai->playback.formats = pdata->snd_fmts;
@@ -404,7 +404,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
}
if (cap & DWC_I2S_RECORD) {
- dev_dbg(&pdev->dev, "SPEAr: record supported\n");
+ dev_dbg(&pdev->dev, "designware: record supported\n");
dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
dw_i2s_dai->capture.channels_max = pdata->channel;
dw_i2s_dai->capture.formats = pdata->snd_fmts;
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3843a18d4e56..aa438546c912 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -108,18 +108,13 @@ if SND_IMX_SOC
config SND_SOC_IMX_SSI
tristate
-config SND_SOC_IMX_PCM
- tristate
-
config SND_SOC_IMX_PCM_FIQ
bool
select FIQ
- select SND_SOC_IMX_PCM
config SND_SOC_IMX_PCM_DMA
bool
select SND_SOC_GENERIC_DMAENGINE_PCM
- select SND_SOC_IMX_PCM
config SND_SOC_IMX_AUDMUX
tristate
@@ -173,6 +168,18 @@ config SND_SOC_EUKREA_TLV320
Enable I2S based access to the TLV320AIC23B codec attached
to the SSI interface
+config SND_SOC_IMX_WM8962
+ tristate "SoC Audio support for i.MX boards with wm8962"
+ depends on OF && I2C
+ select SND_SOC_WM8962
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_UTILS
+ help
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a wm8962 codec.
+
config SND_SOC_IMX_SGTL5000
tristate "SoC Audio support for i.MX boards with sgtl5000"
depends on OF && I2C
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index afd34794db53..d4b4aa8b5649 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -30,18 +30,11 @@ obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
# i.MX Platform Support
snd-soc-imx-ssi-objs := imx-ssi.o
snd-soc-imx-audmux-objs := imx-audmux.o
-snd-soc-imx-pcm-objs := imx-pcm.o
-ifneq ($(CONFIG_SND_SOC_IMX_PCM_FIQ),)
- snd-soc-imx-pcm-objs += imx-pcm-fiq.o
-endif
-ifneq ($(CONFIG_SND_SOC_IMX_PCM_DMA),)
- snd-soc-imx-pcm-objs += imx-pcm-dma.o
-endif
-
obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
-obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
@@ -49,6 +42,7 @@ snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
+snd-soc-imx-wm8962-objs := imx-wm8962.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -56,4 +50,5 @@ obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 75ffdf0e2aad..9a4a0ca2c1de 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -80,7 +80,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = {
.name = "tlv320aic23",
.stream_name = "TLV320AIC23",
.codec_dai_name = "tlv320aic23-hifi",
- .platform_name = "imx-fiq-pcm-audio.0",
+ .platform_name = "imx-ssi.0",
.codec_name = "tlv320aic23-codec.0-001a",
.cpu_dai_name = "imx-ssi.0",
.ops = &eukrea_tlv320_snd_ops,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 0f0bed6def9e..2f2d837df07f 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -122,7 +122,6 @@ struct fsl_ssi_private {
bool new_binding;
bool ssi_on_imx;
struct clk *clk;
- struct platform_device *imx_pcm_pdev;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct imx_dma_data filter_data_tx;
@@ -809,13 +808,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
if (ssi_private->ssi_on_imx) {
- ssi_private->imx_pcm_pdev =
- platform_device_register_simple("imx-pcm-audio",
- -1, NULL, 0);
- if (IS_ERR(ssi_private->imx_pcm_pdev)) {
- ret = PTR_ERR(ssi_private->imx_pcm_pdev);
+ ret = imx_pcm_dma_init(pdev);
+ if (ret)
goto error_dev;
- }
}
/*
@@ -854,7 +849,7 @@ done:
error_dai:
if (ssi_private->ssi_on_imx)
- platform_device_unregister(ssi_private->imx_pcm_pdev);
+ imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
error_dev:
@@ -889,7 +884,7 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
if (ssi_private->ssi_on_imx) {
- platform_device_unregister(ssi_private->imx_pcm_pdev);
+ imx_pcm_dma_exit(pdev);
clk_disable_unprepare(ssi_private->clk);
clk_put(ssi_private->clk);
}
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index 47f046a8fdab..e260f1f899db 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -26,7 +26,6 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/pinctrl/consumer.h>
#include "imx-audmux.h"
@@ -247,7 +246,6 @@ EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
static int imx_audmux_probe(struct platform_device *pdev)
{
struct resource *res;
- struct pinctrl *pinctrl;
const struct of_device_id *of_id =
of_match_device(imx_audmux_dt_ids, &pdev->dev);
@@ -256,12 +254,6 @@ static int imx_audmux_probe(struct platform_device *pdev)
if (IS_ERR(audmux_base))
return PTR_ERR(audmux_base);
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- dev_err(&pdev->dev, "setup pinctrl failed!");
- return PTR_ERR(pinctrl);
- }
-
audmux_clk = devm_clk_get(&pdev->dev, "audmux");
if (IS_ERR(audmux_clk)) {
dev_dbg(&pdev->dev, "cannot get clock: %ld\n",
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index 4ae30f21fdb5..9df173c091a6 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -64,7 +64,7 @@ static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
.codec_dai_name = "mc13783-hifi",
.codec_name = "mc13783-codec",
.cpu_dai_name = "imx-ssi.0",
- .platform_name = "imx-pcm-audio.0",
+ .platform_name = "imx-ssi.0",
.ops = &imx_mc13783_hifi_ops,
.symmetric_rates = 1,
.dai_fmt = FMT_SSI,
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index c246fb514930..fde4d2ea68c8 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -67,8 +67,10 @@ int imx_pcm_dma_init(struct platform_device *pdev)
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
+EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
void imx_pcm_dma_exit(struct platform_device *pdev)
{
snd_dmaengine_pcm_unregister(&pdev->dev);
}
+EXPORT_SYMBOL_GPL(imx_pcm_dma_exit);
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 670b96b0ce2f..310d90290320 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -225,6 +225,22 @@ static int snd_imx_close(struct snd_pcm_substream *substream)
return 0;
}
+static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+
+ pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return ret;
+}
+
static struct snd_pcm_ops imx_pcm_ops = {
.open = snd_imx_open,
.close = snd_imx_close,
@@ -236,6 +252,54 @@ static struct snd_pcm_ops imx_pcm_ops = {
.mmap = snd_imx_pcm_mmap,
};
+static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = IMX_SSI_DMABUF_SIZE;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+
+ return 0;
+}
+
+static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &imx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
static int ssi_irq = 0;
static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
@@ -268,6 +332,27 @@ static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static void imx_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
static void imx_pcm_fiq_free(struct snd_pcm *pcm)
{
mxc_set_irq_fiq(ssi_irq, 0);
@@ -314,3 +399,10 @@ failed_register:
return ret;
}
+EXPORT_SYMBOL_GPL(imx_pcm_fiq_init);
+
+void imx_pcm_fiq_exit(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);
diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
deleted file mode 100644
index c49896442d8e..000000000000
--- a/sound/soc/fsl/imx-pcm.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This code is based on code copyrighted by Freescale,
- * Liam Girdwood, Javier Martin and probably others.
- *
- * 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/dma-mapping.h>
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include "imx-pcm.h"
-
-int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-
- pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap);
-
-static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = IMX_SSI_DMABUF_SIZE;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
-
- return 0;
-}
-
-static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
-
-int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &imx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(imx_pcm_new);
-
-void imx_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-EXPORT_SYMBOL_GPL(imx_pcm_free);
-
-static int imx_pcm_probe(struct platform_device *pdev)
-{
- if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0)
- return imx_pcm_fiq_init(pdev);
-
- return imx_pcm_dma_init(pdev);
-}
-
-static int imx_pcm_remove(struct platform_device *pdev)
-{
- if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0)
- snd_soc_unregister_platform(&pdev->dev);
- else
- imx_pcm_dma_exit(pdev);
-
- return 0;
-}
-
-static struct platform_device_id imx_pcm_devtype[] = {
- { .name = "imx-pcm-audio", },
- { .name = "imx-fiq-pcm-audio", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, imx_pcm_devtype);
-
-static struct platform_driver imx_pcm_driver = {
- .driver = {
- .name = "imx-pcm",
- .owner = THIS_MODULE,
- },
- .id_table = imx_pcm_devtype,
- .probe = imx_pcm_probe,
- .remove = imx_pcm_remove,
-};
-module_platform_driver(imx_pcm_driver);
-
-MODULE_DESCRIPTION("Freescale i.MX PCM driver");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index b7fa0d75c687..67f656c7c320 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -32,11 +32,6 @@ imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data,
dma_data->peripheral_type = IMX_DMATYPE_SSI;
}
-int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma);
-int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
-void imx_pcm_free(struct snd_pcm *pcm);
-
#ifdef CONFIG_SND_SOC_IMX_PCM_DMA
int imx_pcm_dma_init(struct platform_device *pdev);
void imx_pcm_dma_exit(struct platform_device *pdev);
@@ -53,11 +48,16 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev)
#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
int imx_pcm_fiq_init(struct platform_device *pdev);
+void imx_pcm_fiq_exit(struct platform_device *pdev);
#else
static inline int imx_pcm_fiq_init(struct platform_device *pdev)
{
return -ENODEV;
}
+
+static inline void imx_pcm_fiq_exit(struct platform_device *pdev)
+{
+}
#endif
#endif /* _IMX_PCM_H */
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 9584e78858df..7a8bc1220b2e 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -128,28 +128,18 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
goto fail;
}
- data->codec_clk = clk_get(&codec_dev->dev, NULL);
- if (IS_ERR(data->codec_clk)) {
- /* assuming clock enabled by default */
- data->codec_clk = NULL;
- ret = of_property_read_u32(codec_np, "clock-frequency",
- &data->clk_frequency);
- if (ret) {
- dev_err(&codec_dev->dev,
- "clock-frequency missing or invalid\n");
- goto fail;
- }
- } else {
- data->clk_frequency = clk_get_rate(data->codec_clk);
- clk_prepare_enable(data->codec_clk);
- }
+ data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
+ if (IS_ERR(data->codec_clk))
+ goto fail;
+
+ data->clk_frequency = clk_get_rate(data->codec_clk);
data->dai.name = "HiFi";
data->dai.stream_name = "HiFi";
data->dai.codec_dai_name = "sgtl5000";
data->dai.codec_of_node = codec_np;
data->dai.cpu_of_node = ssi_np;
- data->dai.platform_name = "imx-pcm-audio";
+ data->dai.platform_of_node = ssi_np;
data->dai.init = &imx_sgtl5000_dai_init;
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM;
@@ -157,10 +147,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
- goto clk_fail;
+ goto fail;
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret)
- goto clk_fail;
+ goto fail;
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
@@ -170,12 +160,15 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
ret = snd_soc_register_card(&data->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- goto clk_fail;
+ goto fail;
}
platform_set_drvdata(pdev, data);
-clk_fail:
- clk_put(data->codec_clk);
+ of_node_put(ssi_np);
+ of_node_put(codec_np);
+
+ return 0;
+
fail:
if (ssi_np)
of_node_put(ssi_np);
@@ -189,10 +182,6 @@ static int imx_sgtl5000_remove(struct platform_device *pdev)
{
struct imx_sgtl5000_data *data = platform_get_drvdata(pdev);
- if (data->codec_clk) {
- clk_disable_unprepare(data->codec_clk);
- clk_put(data->codec_clk);
- }
snd_soc_unregister_card(&data->card);
return 0;
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index c6fa03e2114a..51be3772cba9 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -501,13 +501,12 @@ static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
imx_ssi_ac97_read(ac97, 0);
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops imx_ssi_ac97_ops = {
.read = imx_ssi_ac97_read,
.write = imx_ssi_ac97_write,
.reset = imx_ssi_ac97_reset,
.warm_reset = imx_ssi_ac97_warm_reset
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int imx_ssi_probe(struct platform_device *pdev)
{
@@ -583,6 +582,12 @@ static int imx_ssi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ssi);
+ ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+ goto failed_register;
+ }
+
ret = snd_soc_register_component(&pdev->dev, &imx_component,
dai, 1);
if (ret) {
@@ -590,46 +595,25 @@ static int imx_ssi_probe(struct platform_device *pdev)
goto failed_register;
}
- ssi->soc_platform_pdev_fiq = platform_device_alloc("imx-fiq-pcm-audio", pdev->id);
- if (!ssi->soc_platform_pdev_fiq) {
- ret = -ENOMEM;
- goto failed_pdev_fiq_alloc;
- }
+ ret = imx_pcm_fiq_init(pdev);
+ if (ret)
+ goto failed_pcm_fiq;
- platform_set_drvdata(ssi->soc_platform_pdev_fiq, ssi);
- ret = platform_device_add(ssi->soc_platform_pdev_fiq);
- if (ret) {
- dev_err(&pdev->dev, "failed to add platform device\n");
- goto failed_pdev_fiq_add;
- }
-
- ssi->soc_platform_pdev = platform_device_alloc("imx-pcm-audio", pdev->id);
- if (!ssi->soc_platform_pdev) {
- ret = -ENOMEM;
- goto failed_pdev_alloc;
- }
-
- platform_set_drvdata(ssi->soc_platform_pdev, ssi);
- ret = platform_device_add(ssi->soc_platform_pdev);
- if (ret) {
- dev_err(&pdev->dev, "failed to add platform device\n");
- goto failed_pdev_add;
- }
+ ret = imx_pcm_dma_init(pdev);
+ if (ret)
+ goto failed_pcm_dma;
return 0;
-failed_pdev_add:
- platform_device_put(ssi->soc_platform_pdev);
-failed_pdev_alloc:
- platform_device_del(ssi->soc_platform_pdev_fiq);
-failed_pdev_fiq_add:
- platform_device_put(ssi->soc_platform_pdev_fiq);
-failed_pdev_fiq_alloc:
+failed_pcm_dma:
+ imx_pcm_fiq_exit(pdev);
+failed_pcm_fiq:
snd_soc_unregister_component(&pdev->dev);
failed_register:
release_mem_region(res->start, resource_size(res));
clk_disable_unprepare(ssi->clk);
failed_clk:
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
@@ -639,8 +623,8 @@ static int imx_ssi_remove(struct platform_device *pdev)
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct imx_ssi *ssi = platform_get_drvdata(pdev);
- platform_device_unregister(ssi->soc_platform_pdev);
- platform_device_unregister(ssi->soc_platform_pdev_fiq);
+ imx_pcm_dma_exit(pdev);
+ imx_pcm_fiq_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
@@ -649,6 +633,7 @@ static int imx_ssi_remove(struct platform_device *pdev)
release_mem_region(res->start, resource_size(res));
clk_disable_unprepare(ssi->clk);
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index bb6b3dbb13fd..d5003cefca8d 100644
--- a/sound/soc/fsl/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
@@ -211,9 +211,6 @@ struct imx_ssi {
struct imx_dma_data filter_data_rx;
int enabled;
-
- struct platform_device *soc_platform_pdev;
- struct platform_device *soc_platform_pdev_fiq;
};
#endif /* _IMX_SSI_H */
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
new file mode 100644
index 000000000000..52a36a90f4f4
--- /dev/null
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on imx-sgtl5000.c
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_i2c.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "../codecs/wm8962.h"
+#include "imx-audmux.h"
+
+#define DAI_NAME_SIZE 32
+
+struct imx_wm8962_data {
+ struct snd_soc_dai_link dai;
+ struct snd_soc_card card;
+ char codec_dai_name[DAI_NAME_SIZE];
+ char platform_name[DAI_NAME_SIZE];
+ struct clk *codec_clk;
+ unsigned int clk_frequency;
+};
+
+struct imx_priv {
+ struct platform_device *pdev;
+};
+static struct imx_priv card_priv;
+
+static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static int sample_rate = 44100;
+static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ sample_rate = params_rate(params);
+ sample_format = params_format(params);
+
+ return 0;
+}
+
+static struct snd_soc_ops imx_hifi_ops = {
+ .hw_params = imx_hifi_hw_params,
+};
+
+static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct imx_priv *priv = &card_priv;
+ struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+ struct device *dev = &priv->pdev->dev;
+ unsigned int pll_out;
+ int ret;
+
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = sample_rate * 384;
+ else
+ pll_out = sample_rate * 256;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+ WM8962_FLL_MCLK, data->clk_frequency,
+ pll_out);
+ if (ret < 0) {
+ dev_err(dev, "failed to start FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ WM8962_SYSCLK_FLL, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level == SND_SOC_BIAS_PREPARE) {
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ WM8962_SYSCLK_MCLK, data->clk_frequency,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(dev,
+ "failed to switch away from FLL: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+ 0, 0, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dapm->bias_level = level;
+
+ return 0;
+}
+
+static int imx_wm8962_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct imx_priv *priv = &card_priv;
+ struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+ struct device *dev = &priv->pdev->dev;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
+ data->clk_frequency, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+
+ return ret;
+}
+
+static int imx_wm8962_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *ssi_np, *codec_np;
+ struct platform_device *ssi_pdev;
+ struct imx_priv *priv = &card_priv;
+ struct i2c_client *codec_dev;
+ struct imx_wm8962_data *data;
+ int int_port, ext_port;
+ int ret;
+
+ priv->pdev = pdev;
+
+ ret = of_property_read_u32(np, "mux-int-port", &int_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
+ return ret;
+ }
+
+ /*
+ * The port numbering in the hardware manual starts at 1, while
+ * the audmux API expects it starts at 0.
+ */
+ int_port--;
+ ext_port--;
+ ret = imx_audmux_v2_configure_port(int_port,
+ IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+ if (ret) {
+ dev_err(&pdev->dev, "audmux internal port setup failed\n");
+ return ret;
+ }
+ imx_audmux_v2_configure_port(ext_port,
+ IMX_AUDMUX_V2_PTCR_SYN,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+ if (ret) {
+ dev_err(&pdev->dev, "audmux external port setup failed\n");
+ return ret;
+ }
+
+ ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
+ codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+ if (!ssi_np || !codec_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ssi_pdev = of_find_device_by_node(ssi_np);
+ if (!ssi_pdev) {
+ dev_err(&pdev->dev, "failed to find SSI platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ if (!codec_dev || !codec_dev->driver) {
+ dev_err(&pdev->dev, "failed to find codec platform device\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
+ if (IS_ERR(data->codec_clk)) {
+ ret = PTR_ERR(data->codec_clk);
+ dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
+ goto fail;
+ }
+
+ data->clk_frequency = clk_get_rate(data->codec_clk);
+ ret = clk_prepare_enable(data->codec_clk);
+ if (ret) {
+ dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret);
+ goto fail;
+ }
+
+ data->dai.name = "HiFi";
+ data->dai.stream_name = "HiFi";
+ data->dai.codec_dai_name = "wm8962";
+ data->dai.codec_of_node = codec_np;
+ data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
+ data->dai.platform_of_node = ssi_np;
+ data->dai.ops = &imx_hifi_ops;
+ data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ data->card.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto clk_fail;
+ ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+ if (ret)
+ goto clk_fail;
+ data->card.num_links = 1;
+ data->card.dai_link = &data->dai;
+ data->card.dapm_widgets = imx_wm8962_dapm_widgets;
+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8962_dapm_widgets);
+
+ data->card.late_probe = imx_wm8962_late_probe;
+ data->card.set_bias_level = imx_wm8962_set_bias_level;
+
+ ret = snd_soc_register_card(&data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto clk_fail;
+ }
+
+ platform_set_drvdata(pdev, data);
+ of_node_put(ssi_np);
+ of_node_put(codec_np);
+
+ return 0;
+
+clk_fail:
+ if (!IS_ERR(data->codec_clk))
+ clk_disable_unprepare(data->codec_clk);
+fail:
+ if (ssi_np)
+ of_node_put(ssi_np);
+ if (codec_np)
+ of_node_put(codec_np);
+
+ return ret;
+}
+
+static int imx_wm8962_remove(struct platform_device *pdev)
+{
+ struct imx_wm8962_data *data = platform_get_drvdata(pdev);
+
+ if (!IS_ERR(data->codec_clk))
+ clk_disable_unprepare(data->codec_clk);
+ snd_soc_unregister_card(&data->card);
+
+ return 0;
+}
+
+static const struct of_device_id imx_wm8962_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-wm8962", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_wm8962_dt_ids);
+
+static struct platform_driver imx_wm8962_driver = {
+ .driver = {
+ .name = "imx-wm8962",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_wm8962_dt_ids,
+ },
+ .probe = imx_wm8962_probe,
+ .remove = imx_wm8962_remove,
+};
+module_platform_driver(imx_wm8962_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX WM8962 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-wm8962");
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 4141b35ef0bb..3ef7a0c92efa 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -131,13 +131,12 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
psc_ac97_warm_reset(ac97);
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops psc_ac97_ops = {
.read = psc_ac97_read,
.write = psc_ac97_write,
.reset = psc_ac97_cold_reset,
.warm_reset = psc_ac97_warm_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -290,6 +289,12 @@ static int psc_ac97_of_probe(struct platform_device *op)
if (rc != 0)
return rc;
+ rc = snd_soc_set_ac97_ops(&psc_ac97_ops);
+ if (rc != 0) {
+ dev_err(&op->dev, "Failed to set AC'97 ops: %d\n", ret);
+ return rc;
+ }
+
rc = snd_soc_register_component(&op->dev, &psc_ac97_component,
psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
if (rc != 0) {
@@ -318,6 +323,7 @@ static int psc_ac97_of_remove(struct platform_device *op)
{
mpc5200_audio_dma_destroy(op);
snd_soc_unregister_component(&op->dev);
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
index 3d1074179057..f4c3bda5e69e 100644
--- a/sound/soc/fsl/mx27vis-aic32x4.c
+++ b/sound/soc/fsl/mx27vis-aic32x4.c
@@ -161,7 +161,7 @@ static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
.name = "tlv320aic32x4",
.stream_name = "TLV320AIC32X4",
.codec_dai_name = "tlv320aic32x4-hifi",
- .platform_name = "imx-pcm-audio.0",
+ .platform_name = "imx-ssi.0",
.codec_name = "tlv320aic32x4.0-0018",
.cpu_dai_name = "imx-ssi.0",
.ops = &mx27vis_aic32x4_snd_ops,
diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
index f8da6dd115ed..ae403c29688f 100644
--- a/sound/soc/fsl/phycore-ac97.c
+++ b/sound/soc/fsl/phycore-ac97.c
@@ -33,7 +33,7 @@ static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
.codec_dai_name = "wm9712-hifi",
.codec_name = "wm9712-codec",
.cpu_dai_name = "imx-ssi.0",
- .platform_name = "imx-fiq-pcm-audio.0",
+ .platform_name = "imx-ssi.0",
.ops = &imx_phycore_hifi_ops,
},
};
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
index fe54a69073e5..fce63252bdbb 100644
--- a/sound/soc/fsl/wm1133-ev1.c
+++ b/sound/soc/fsl/wm1133-ev1.c
@@ -245,7 +245,7 @@ static struct snd_soc_dai_link wm1133_ev1_dai = {
.stream_name = "Audio",
.cpu_dai_name = "imx-ssi.0",
.codec_dai_name = "wm8350-hifi",
- .platform_name = "imx-fiq-pcm-audio.0",
+ .platform_name = "imx-ssi.0",
.codec_name = "wm8350-codec.0-0x1a",
.init = wm1133_ev1_init,
.ops = &wm1133_ev1_ops,
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 9a126441c5f3..4c849a49c72a 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -118,7 +118,7 @@ static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
ctrl |= JZ_AIC_CTRL_FLUSH;
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
- clk_enable(i2s->clk_i2s);
+ clk_prepare_enable(i2s->clk_i2s);
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf |= JZ_AIC_CONF_ENABLE;
@@ -140,7 +140,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
conf &= ~JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
- clk_disable(i2s->clk_i2s);
+ clk_disable_unprepare(i2s->clk_i2s);
}
static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -314,10 +314,10 @@ static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
conf &= ~JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
- clk_disable(i2s->clk_i2s);
+ clk_disable_unprepare(i2s->clk_i2s);
}
- clk_disable(i2s->clk_aic);
+ clk_disable_unprepare(i2s->clk_aic);
return 0;
}
@@ -327,10 +327,10 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai)
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf;
- clk_enable(i2s->clk_aic);
+ clk_prepare_enable(i2s->clk_aic);
if (dai->active) {
- clk_enable(i2s->clk_i2s);
+ clk_prepare_enable(i2s->clk_i2s);
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf |= JZ_AIC_CONF_ENABLE;
@@ -368,7 +368,7 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf;
- clk_enable(i2s->clk_aic);
+ clk_prepare_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s);
@@ -388,7 +388,7 @@ static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- clk_disable(i2s->clk_aic);
+ clk_disable_unprepare(i2s->clk_aic);
return 0;
}
@@ -509,7 +509,6 @@ static int jz4740_i2s_dev_remove(struct platform_device *pdev)
iounmap(i2s->base);
release_mem_region(i2s->mem->start, resource_size(i2s->mem));
- platform_set_drvdata(pdev, NULL);
kfree(i2s);
return 0;
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index d3d4bdca1cc6..a9f14530c3db 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -289,7 +289,7 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
return count;
}
-struct snd_pcm_ops kirkwood_dma_ops = {
+static struct snd_pcm_ops kirkwood_dma_ops = {
.open = kirkwood_dma_open,
.close = kirkwood_dma_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c
index 4139116c33b5..ee363845759e 100644
--- a/sound/soc/mid-x86/mfld_machine.c
+++ b/sound/soc/mid-x86/mfld_machine.c
@@ -371,7 +371,7 @@ static int snd_mfld_mc_probe(struct platform_device *pdev)
/* audio interrupt base of SRAM location where
* interrupts are stored by System FW */
- mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
+ mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC);
if (!mc_drv_ctx) {
pr_err("allocation failed\n");
return -ENOMEM;
@@ -381,51 +381,39 @@ static int snd_mfld_mc_probe(struct platform_device *pdev)
pdev, IORESOURCE_MEM, "IRQ_BASE");
if (!irq_mem) {
pr_err("no mem resource given\n");
- ret_val = -ENODEV;
- goto unalloc;
+ return -ENODEV;
}
- mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
- resource_size(irq_mem));
+ mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start,
+ resource_size(irq_mem));
if (!mc_drv_ctx->int_base) {
pr_err("Mapping of cache failed\n");
- ret_val = -ENOMEM;
- goto unalloc;
+ return -ENOMEM;
}
/* register for interrupt */
- ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
+ ret_val = devm_request_threaded_irq(&pdev->dev, irq,
+ snd_mfld_jack_intr_handler,
snd_mfld_jack_detection,
IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
if (ret_val) {
pr_err("cannot register IRQ\n");
- goto unalloc;
+ return ret_val;
}
/* register the soc card */
snd_soc_card_mfld.dev = &pdev->dev;
ret_val = snd_soc_register_card(&snd_soc_card_mfld);
if (ret_val) {
pr_debug("snd_soc_register_card failed %d\n", ret_val);
- goto freeirq;
+ return ret_val;
}
platform_set_drvdata(pdev, mc_drv_ctx);
pr_debug("successfully exited probe\n");
- return ret_val;
-
-freeirq:
- free_irq(irq, mc_drv_ctx);
-unalloc:
- kfree(mc_drv_ctx);
- return ret_val;
+ return 0;
}
static int snd_mfld_mc_remove(struct platform_device *pdev)
{
- struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
-
pr_debug("snd_mfld_mc_remove called\n");
- free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
snd_soc_unregister_card(&snd_soc_card_mfld);
- kfree(mc_drv_ctx);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index b41fffc056fb..b16abbbf7764 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -49,24 +49,8 @@ static const struct snd_pcm_hardware snd_mxs_hardware = {
.fifo_size = 32,
};
-static bool filter(struct dma_chan *chan, void *param)
-{
- struct mxs_pcm_dma_params *dma_params = param;
-
- if (!mxs_dma_is_apbx(chan))
- return false;
-
- if (chan->chan_id != dma_params->chan_num)
- return false;
-
- chan->private = &dma_params->dma_data;
-
- return true;
-}
-
static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = {
.pcm_hardware = &snd_mxs_hardware,
- .compat_filter_fn = filter,
.prealloc_buffer_size = 64 * 1024,
};
@@ -74,8 +58,6 @@ int mxs_pcm_platform_register(struct device *dev)
{
return snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
- SND_DMAENGINE_PCM_FLAG_NO_DT |
- SND_DMAENGINE_PCM_FLAG_COMPAT |
SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
}
EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
index 3aa918f9ed3e..bc685b67cac7 100644
--- a/sound/soc/mxs/mxs-pcm.h
+++ b/sound/soc/mxs/mxs-pcm.h
@@ -19,13 +19,6 @@
#ifndef _MXS_PCM_H
#define _MXS_PCM_H
-#include <linux/fsl/mxs-dma.h>
-
-struct mxs_pcm_dma_params {
- struct mxs_dma_data dma_data;
- int chan_num;
-};
-
int mxs_pcm_platform_register(struct device *dev);
void mxs_pcm_platform_unregister(struct device *dev);
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index d31dc52fa862..49d870034bc3 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -26,8 +26,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/fsl/mxs-dma.h>
-#include <linux/pinctrl/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -605,8 +603,6 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai)
struct mxs_saif *saif = dev_get_drvdata(dai->dev);
snd_soc_dai_set_drvdata(dai, saif);
- dai->playback_dma_data = &saif->dma_param;
- dai->capture_dma_data = &saif->dma_param;
return 0;
}
@@ -665,9 +661,8 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
static int mxs_saif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct resource *iores, *dmares;
+ struct resource *iores;
struct mxs_saif *saif;
- struct pinctrl *pinctrl;
int ret = 0;
struct device_node *master;
@@ -707,12 +702,6 @@ static int mxs_saif_probe(struct platform_device *pdev)
mxs_saif[saif->id] = saif;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- return ret;
- }
-
saif->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(saif->clk)) {
ret = PTR_ERR(saif->clk);
@@ -727,22 +716,6 @@ static int mxs_saif_probe(struct platform_device *pdev)
if (IS_ERR(saif->base))
return PTR_ERR(saif->base);
- dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmares) {
- /*
- * TODO: This is a temporary solution and should be changed
- * to use generic DMA binding later when the helplers get in.
- */
- ret = of_property_read_u32(np, "fsl,saif-dma-channel",
- &saif->dma_param.chan_num);
- if (ret) {
- dev_err(&pdev->dev, "failed to get dma channel\n");
- return ret;
- }
- } else {
- saif->dma_param.chan_num = dmares->start;
- }
-
saif->irq = platform_get_irq(pdev, 0);
if (saif->irq < 0) {
ret = saif->irq;
@@ -759,14 +732,6 @@ static int mxs_saif_probe(struct platform_device *pdev)
return ret;
}
- saif->dma_param.dma_data.chan_irq = platform_get_irq(pdev, 1);
- if (saif->dma_param.dma_data.chan_irq < 0) {
- ret = saif->dma_param.dma_data.chan_irq;
- dev_err(&pdev->dev, "failed to get dma irq resource: %d\n",
- ret);
- return ret;
- }
-
platform_set_drvdata(pdev, saif);
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h
index 3cb342e5bc90..53eaa4bf0e27 100644
--- a/sound/soc/mxs/mxs-saif.h
+++ b/sound/soc/mxs/mxs-saif.h
@@ -117,7 +117,6 @@ struct mxs_saif {
unsigned int mclk_in_use;
void __iomem *base;
int irq;
- struct mxs_pcm_dma_params dma_param;
unsigned int id;
unsigned int master_id;
unsigned int cur_rate;
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index b1d9b5ebeeeb..1b134d72f120 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -90,17 +90,11 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
.name = "HiFi Tx",
.stream_name = "HiFi Playback",
.codec_dai_name = "sgtl5000",
- .codec_name = "sgtl5000.0-000a",
- .cpu_dai_name = "mxs-saif.0",
- .platform_name = "mxs-saif.0",
.ops = &mxs_sgtl5000_hifi_ops,
}, {
.name = "HiFi Rx",
.stream_name = "HiFi Capture",
.codec_dai_name = "sgtl5000",
- .codec_name = "sgtl5000.0-000a",
- .cpu_dai_name = "mxs-saif.1",
- .platform_name = "mxs-saif.1",
.ops = &mxs_sgtl5000_hifi_ops,
},
};
@@ -116,7 +110,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *saif_np[2], *codec_np;
- int i, ret = 0;
+ int i;
if (!np)
return 1; /* no device tree */
@@ -142,7 +136,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev)
of_node_put(saif_np[0]);
of_node_put(saif_np[1]);
- return ret;
+ return 0;
}
static int mxs_sgtl5000_probe(struct platform_device *pdev)
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index fe3285ceaf5b..f4c2417a8730 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -197,13 +197,12 @@ static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
}
/* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops nuc900_ac97_ops = {
.read = nuc900_ac97_read,
.write = nuc900_ac97_write,
.reset = nuc900_ac97_cold_reset,
.warm_reset = nuc900_ac97_warm_reset,
-}
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
+};
static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
@@ -326,64 +325,52 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev)
if (nuc900_ac97_data)
return -EBUSY;
- nuc900_audio = kzalloc(sizeof(struct nuc900_audio), GFP_KERNEL);
+ nuc900_audio = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_audio),
+ GFP_KERNEL);
if (!nuc900_audio)
return -ENOMEM;
spin_lock_init(&nuc900_audio->lock);
nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!nuc900_audio->res) {
- ret = -ENODEV;
- goto out0;
- }
-
- if (!request_mem_region(nuc900_audio->res->start,
- resource_size(nuc900_audio->res), pdev->name)) {
- ret = -EBUSY;
- goto out0;
- }
+ if (!nuc900_audio->res)
+ return ret;
- nuc900_audio->mmio = ioremap(nuc900_audio->res->start,
- resource_size(nuc900_audio->res));
- if (!nuc900_audio->mmio) {
- ret = -ENOMEM;
- goto out1;
- }
+ nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
+ nuc900_audio->res);
+ if (IS_ERR(nuc900_audio->mmio))
+ return PTR_ERR(nuc900_audio->mmio);
- nuc900_audio->clk = clk_get(&pdev->dev, NULL);
+ nuc900_audio->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_audio->clk)) {
ret = PTR_ERR(nuc900_audio->clk);
- goto out2;
+ goto out;
}
nuc900_audio->irq_num = platform_get_irq(pdev, 0);
if (!nuc900_audio->irq_num) {
ret = -EBUSY;
- goto out3;
+ goto out;
}
nuc900_ac97_data = nuc900_audio;
+ ret = snd_soc_set_ac97_ops(&nuc900_ac97_ops);
+ if (ret)
+ goto out;
+
ret = snd_soc_register_component(&pdev->dev, &nuc900_ac97_component,
&nuc900_ac97_dai, 1);
if (ret)
- goto out3;
+ goto out;
/* enbale ac97 multifunction pin */
mfp_set_groupg(nuc900_audio->dev, NULL);
return 0;
-out3:
- clk_put(nuc900_audio->clk);
-out2:
- iounmap(nuc900_audio->mmio);
-out1:
- release_mem_region(nuc900_audio->res->start,
- resource_size(nuc900_audio->res));
-out0:
- kfree(nuc900_audio);
+out:
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
@@ -391,13 +378,8 @@ static int nuc900_ac97_drvremove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
- clk_put(nuc900_ac97_data->clk);
- iounmap(nuc900_ac97_data->mmio);
- release_mem_region(nuc900_ac97_data->res->start,
- resource_size(nuc900_ac97_data->res));
-
- kfree(nuc900_ac97_data);
nuc900_ac97_data = NULL;
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 60259f2f3f2c..9f5d55e6b17a 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -103,7 +103,7 @@ config SND_OMAP_SOC_OMAP_HDMI
tristate "SoC Audio support for Texas Instruments OMAP HDMI"
depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS
select SND_OMAP_SOC_HDMI
- select SND_SOC_OMAP_HDMI_CODEC
+ select SND_SOC_HDMI_CODEC
select OMAP4_DSS_HDMI_AUDIO
help
Say Y if you want to add support for SoC HDMI audio on Texas Instruments
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 2b225945359b..a725905b2c68 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -26,7 +26,6 @@ obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o
obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
diff --git a/sound/soc/omap/omap-hdmi-card.c b/sound/soc/omap/omap-hdmi-card.c
index d4eaa92e518e..7e66e9cba5a8 100644
--- a/sound/soc/omap/omap-hdmi-card.c
+++ b/sound/soc/omap/omap-hdmi-card.c
@@ -35,7 +35,7 @@ static struct snd_soc_dai_link omap_hdmi_dai = {
.cpu_dai_name = "omap-hdmi-audio-dai",
.platform_name = "omap-pcm-audio",
.codec_name = "hdmi-audio-codec",
- .codec_dai_name = "omap-hdmi-hifi",
+ .codec_dai_name = "hdmi-hifi",
};
static struct snd_soc_card snd_soc_omap_hdmi = {
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index eadbfb6b5000..7483efb6dc67 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -814,8 +814,6 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
clk_put(mcbsp->fclk);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 4d2e46fae77c..b35809467547 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -130,26 +130,6 @@ config SND_PXA2XX_SOC_PALM27X
Say Y if you want to add support for SoC audio on
Palm T|X, T5, E2 or LifeDrive handheld computer.
-config SND_SOC_SAARB
- tristate "SoC Audio support for Marvell Saarb"
- depends on SND_PXA2XX_SOC && MACH_SAARB
- select MFD_88PM860X
- select SND_PXA_SOC_SSP
- select SND_SOC_88PM860X
- help
- Say Y if you want to add support for SoC audio on the
- Marvell Saarb reference platform.
-
-config SND_SOC_TAVOREVB3
- tristate "SoC Audio support for Marvell Tavor EVB3"
- depends on SND_PXA2XX_SOC && MACH_TAVOREVB3
- select MFD_88PM860X
- select SND_PXA_SOC_SSP
- select SND_SOC_88PM860X
- help
- Say Y if you want to add support for SoC audio on the
- Marvell Saarb reference platform.
-
config SND_PXA910_SOC
tristate "SoC Audio for Marvell PXA910 chip"
depends on ARCH_MMP && SND
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index d8a265d2d5d7..2cff67b61dc3 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -23,8 +23,6 @@ snd-soc-e800-objs := e800_wm9712.o
snd-soc-spitz-objs := spitz.o
snd-soc-em-x270-objs := em-x270.o
snd-soc-palm27x-objs := palm27x.o
-snd-soc-saarb-objs := saarb.o
-snd-soc-tavorevb3-objs := tavorevb3.o
snd-soc-zylonite-objs := zylonite.o
snd-soc-hx4700-objs := hx4700.o
snd-soc-magician-objs := magician.o
@@ -48,8 +46,6 @@ obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
-obj-$(CONFIG_SND_SOC_SAARB) += snd-soc-saarb.o
-obj-$(CONFIG_SND_SOC_TAVOREVB3) += snd-soc-tavorevb3.o
obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 349930015264..5d57e071cdf5 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -147,7 +147,7 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
-struct snd_pcm_ops mmp_pcm_ops = {
+static struct snd_pcm_ops mmp_pcm_ops = {
.open = mmp_pcm_open,
.close = snd_dmaengine_pcm_close_release_chan,
.ioctl = snd_pcm_lib_ioctl,
@@ -208,7 +208,7 @@ static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
return 0;
}
-int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm_substream *substream;
struct snd_pcm *pcm = rtd->pcm;
@@ -229,7 +229,7 @@ err:
return ret;
}
-struct snd_soc_platform_driver mmp_soc_platform = {
+static struct snd_soc_platform_driver mmp_soc_platform = {
.ops = &mmp_pcm_ops,
.pcm_new = mmp_pcm_new,
.pcm_free = mmp_pcm_free_dma_buffers,
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index a64779980177..62142ce367c7 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -388,7 +388,7 @@ static struct snd_soc_dai_ops mmp_sspa_dai_ops = {
.set_fmt = mmp_sspa_set_dai_fmt,
};
-struct snd_soc_dai_driver mmp_sspa_dai = {
+static struct snd_soc_dai_driver mmp_sspa_dai = {
.probe = mmp_sspa_probe,
.playback = {
.channels_min = 1,
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 57ea8e6c5488..1475515712e6 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -41,13 +41,12 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
pxa2xx_ac97_finish_reset(ac97);
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.read = pxa2xx_ac97_read,
.write = pxa2xx_ac97_write,
.warm_reset = pxa2xx_ac97_warm_reset,
.reset = pxa2xx_ac97_cold_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
.name = "AC97 PCM Stereo out",
@@ -239,11 +238,17 @@ static const struct snd_soc_component_driver pxa_ac97_component = {
static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
+ int ret;
+
if (pdev->id != -1) {
dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
return -ENXIO;
}
+ ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops);
+ if (ret != 0)
+ return ret;
+
/* Punt most of the init to the SoC probe; we may need the machine
* driver to do interesting things with the clocking to get us up
* and running.
@@ -255,6 +260,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index eda891e6f31b..a49c21ba3842 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,4 @@
#define PXA2XX_DAI_AC97_AUX 1
#define PXA2XX_DAI_AC97_MIC 2
-/* platform data */
-extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
-
#endif
diff --git a/sound/soc/pxa/saarb.c b/sound/soc/pxa/saarb.c
deleted file mode 100644
index c34146b776b4..000000000000
--- a/sound/soc/pxa/saarb.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * saarb.c -- SoC audio for saarb
- *
- * Copyright (C) 2010 Marvell International Ltd.
- * Haojian Zhuang <haojian.zhuang@marvell.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/device.h>
-#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/88pm860x-codec.h"
-#include "pxa-ssp.h"
-
-static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd);
-
-static struct platform_device *saarb_snd_device;
-
-static struct snd_soc_jack hs_jack, mic_jack;
-
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, },
-};
-
-static struct snd_soc_jack_pin mic_jack_pins[] = {
- { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, },
-};
-
-/* saarb machine dapm widgets */
-static const struct snd_soc_dapm_widget saarb_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Stereophone", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 1", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 2", NULL),
- SND_SOC_DAPM_SPK("Ext Speaker", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 1", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 3", NULL),
-};
-
-/* saarb machine audio map */
-static const struct snd_soc_dapm_route saarb_audio_map[] = {
- {"Headset Stereophone", NULL, "HS1"},
- {"Headset Stereophone", NULL, "HS2"},
-
- {"Ext Speaker", NULL, "LSP"},
- {"Ext Speaker", NULL, "LSN"},
-
- {"Lineout Out 1", NULL, "LINEOUT1"},
- {"Lineout Out 2", NULL, "LINEOUT2"},
-
- {"MIC1P", NULL, "Mic1 Bias"},
- {"MIC1N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Ext Mic 1"},
-
- {"MIC2P", NULL, "Mic1 Bias"},
- {"MIC2N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Headset Mic 2"},
-
- {"MIC3P", NULL, "Mic3 Bias"},
- {"MIC3N", NULL, "Mic3 Bias"},
- {"Mic3 Bias", NULL, "Ext Mic 3"},
-};
-
-static int saarb_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int width = snd_pcm_format_physical_width(params_format(params));
- int ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0,
- PM860X_CLK_DIR_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width);
-
- return ret;
-}
-
-static struct snd_soc_ops saarb_i2s_ops = {
- .hw_params = saarb_i2s_hw_params,
-};
-
-static struct snd_soc_dai_link saarb_dai[] = {
- {
- .name = "88PM860x I2S",
- .stream_name = "I2S Audio",
- .cpu_dai_name = "pxa-ssp-dai.1",
- .codec_dai_name = "88pm860x-i2s",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "88pm860x-codec",
- .init = saarb_pm860x_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &saarb_i2s_ops,
- },
-};
-
-static struct snd_soc_card snd_soc_card_saarb = {
- .name = "Saarb",
- .owner = THIS_MODULE,
- .dai_link = saarb_dai,
- .num_links = ARRAY_SIZE(saarb_dai),
-
- .dapm_widgets = saarb_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(saarb_dapm_widgets),
- .dapm_routes = saarb_audio_map,
- .num_dapm_routes = ARRAY_SIZE(saarb_audio_map),
-};
-
-static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- /* connected pins */
- snd_soc_dapm_enable_pin(dapm, "Ext Speaker");
- snd_soc_dapm_enable_pin(dapm, "Ext Mic 1");
- snd_soc_dapm_enable_pin(dapm, "Ext Mic 3");
- snd_soc_dapm_disable_pin(dapm, "Headset Mic 2");
- snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
-
- /* Headset jack detection */
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE
- | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &hs_jack);
- snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
- snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE,
- &mic_jack);
- snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
- mic_jack_pins);
-
- /* headphone, microphone detection & headset short detection */
- pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE,
- SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2);
- pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE);
- return 0;
-}
-
-static int __init saarb_init(void)
-{
- int ret;
-
- if (!machine_is_saarb())
- return -ENODEV;
- saarb_snd_device = platform_device_alloc("soc-audio", -1);
- if (!saarb_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(saarb_snd_device, &snd_soc_card_saarb);
-
- ret = platform_device_add(saarb_snd_device);
- if (ret)
- platform_device_put(saarb_snd_device);
-
- return ret;
-}
-
-static void __exit saarb_exit(void)
-{
- platform_device_unregister(saarb_snd_device);
-}
-
-module_init(saarb_init);
-module_exit(saarb_exit);
-
-MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
-MODULE_DESCRIPTION("ALSA SoC 88PM860x Saarb");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/tavorevb3.c b/sound/soc/pxa/tavorevb3.c
deleted file mode 100644
index 8b5ab8f72726..000000000000
--- a/sound/soc/pxa/tavorevb3.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * tavorevb3.c -- SoC audio for Tavor EVB3
- *
- * Copyright (C) 2010 Marvell International Ltd.
- * Haojian Zhuang <haojian.zhuang@marvell.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/device.h>
-#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-
-#include "../codecs/88pm860x-codec.h"
-#include "pxa-ssp.h"
-
-static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd);
-
-static struct platform_device *evb3_snd_device;
-
-static struct snd_soc_jack hs_jack, mic_jack;
-
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, },
-};
-
-static struct snd_soc_jack_pin mic_jack_pins[] = {
- { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, },
-};
-
-/* tavorevb3 machine dapm widgets */
-static const struct snd_soc_dapm_widget evb3_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headset Stereophone", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 1", NULL),
- SND_SOC_DAPM_LINE("Lineout Out 2", NULL),
- SND_SOC_DAPM_SPK("Ext Speaker", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 1", NULL),
- SND_SOC_DAPM_MIC("Headset Mic 2", NULL),
- SND_SOC_DAPM_MIC("Ext Mic 3", NULL),
-};
-
-/* tavorevb3 machine audio map */
-static const struct snd_soc_dapm_route evb3_audio_map[] = {
- {"Headset Stereophone", NULL, "HS1"},
- {"Headset Stereophone", NULL, "HS2"},
-
- {"Ext Speaker", NULL, "LSP"},
- {"Ext Speaker", NULL, "LSN"},
-
- {"Lineout Out 1", NULL, "LINEOUT1"},
- {"Lineout Out 2", NULL, "LINEOUT2"},
-
- {"MIC1P", NULL, "Mic1 Bias"},
- {"MIC1N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Ext Mic 1"},
-
- {"MIC2P", NULL, "Mic1 Bias"},
- {"MIC2N", NULL, "Mic1 Bias"},
- {"Mic1 Bias", NULL, "Headset Mic 2"},
-
- {"MIC3P", NULL, "Mic3 Bias"},
- {"MIC3N", NULL, "Mic3 Bias"},
- {"Mic3 Bias", NULL, "Ext Mic 3"},
-};
-
-static int evb3_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int width = snd_pcm_format_physical_width(params_format(params));
- int ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0,
- PM860X_CLK_DIR_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width);
- return ret;
-}
-
-static struct snd_soc_ops evb3_i2s_ops = {
- .hw_params = evb3_i2s_hw_params,
-};
-
-static struct snd_soc_dai_link evb3_dai[] = {
- {
- .name = "88PM860x I2S",
- .stream_name = "I2S Audio",
- .cpu_dai_name = "pxa-ssp-dai.1",
- .codec_dai_name = "88pm860x-i2s",
- .platform_name = "pxa-pcm-audio",
- .codec_name = "88pm860x-codec",
- .init = evb3_pm860x_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &evb3_i2s_ops,
- },
-};
-
-static struct snd_soc_card snd_soc_card_evb3 = {
- .name = "Tavor EVB3",
- .owner = THIS_MODULE,
- .dai_link = evb3_dai,
- .num_links = ARRAY_SIZE(evb3_dai),
-
- .dapm_widgets = evb3_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(evb3_dapm_widgets),
- .dapm_routes = evb3_audio_map,
- .num_dapm_routes = ARRAY_SIZE(evb3_audio_map),
-};
-
-static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- /* connected pins */
- snd_soc_dapm_enable_pin(dapm, "Ext Speaker");
- snd_soc_dapm_enable_pin(dapm, "Ext Mic 1");
- snd_soc_dapm_enable_pin(dapm, "Ext Mic 3");
- snd_soc_dapm_disable_pin(dapm, "Headset Mic 2");
- snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
-
- /* Headset jack detection */
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE
- | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &hs_jack);
- snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
- snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE,
- &mic_jack);
- snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
- mic_jack_pins);
-
- /* headphone, microphone detection & headset short detection */
- pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE,
- SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2);
- pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE);
- return 0;
-}
-
-static int __init tavorevb3_init(void)
-{
- int ret;
-
- if (!machine_is_tavorevb3())
- return -ENODEV;
- evb3_snd_device = platform_device_alloc("soc-audio", -1);
- if (!evb3_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(evb3_snd_device, &snd_soc_card_evb3);
-
- ret = platform_device_add(evb3_snd_device);
- if (ret)
- platform_device_put(evb3_snd_device);
-
- return ret;
-}
-
-static void __exit tavorevb3_exit(void)
-{
- platform_device_unregister(evb3_snd_device);
-}
-
-module_init(tavorevb3_init);
-module_exit(tavorevb3_exit);
-
-MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
-MODULE_DESCRIPTION("ALSA SoC 88PM860x Tavor EVB3");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index ceb656695b0f..db8aadf8932d 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -256,7 +256,6 @@ static struct snd_soc_card zylonite = {
.resume_pre = &zylonite_resume_pre,
.dai_link = zylonite_dai,
.num_links = ARRAY_SIZE(zylonite_dai),
- .owner = THIS_MODULE,
};
static struct platform_device *zylonite_snd_ac97_device;
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 475fb0d8b3c6..9855dfc3e3ec 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -39,7 +39,7 @@ config SND_SOC_SAMSUNG_NEO1973_WM8753
depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
select SND_S3C24XX_I2S
select SND_SOC_WM8753
- select SND_SOC_DFBMCS320
+ select SND_SOC_BT_SCO
help
Say Y here to enable audio support for the Openmoko Neo1973
Smartphones.
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index cb88ead98917..2dd623fa3882 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -214,13 +214,12 @@ static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops s3c_ac97_ops = {
.read = s3c_ac97_read,
.write = s3c_ac97_write,
.warm_reset = s3c_ac97_warm_reset,
.reset = s3c_ac97_cold_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -417,11 +416,9 @@ static int s3c_ac97_probe(struct platform_device *pdev)
return -ENXIO;
}
- if (!request_mem_region(mem_res->start,
- resource_size(mem_res), "ac97")) {
- dev_err(&pdev->dev, "Unable to request register region\n");
- return -EBUSY;
- }
+ s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res);
+ if (IS_ERR(s3c_ac97.regs))
+ return PTR_ERR(s3c_ac97.regs);
s3c_ac97_pcm_out.channel = dmatx_res->start;
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
@@ -433,14 +430,7 @@ static int s3c_ac97_probe(struct platform_device *pdev)
init_completion(&s3c_ac97.done);
mutex_init(&s3c_ac97.lock);
- s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
- if (s3c_ac97.regs == NULL) {
- dev_err(&pdev->dev, "Unable to ioremap register region\n");
- ret = -ENXIO;
- goto err1;
- }
-
- s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+ s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97");
if (IS_ERR(s3c_ac97.ac97_clk)) {
dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n");
ret = -ENODEV;
@@ -461,6 +451,12 @@ static int s3c_ac97_probe(struct platform_device *pdev)
goto err4;
}
+ ret = snd_soc_set_ac97_ops(&s3c_ac97_ops);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+ goto err4;
+ }
+
ret = snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
if (ret)
@@ -480,18 +476,14 @@ err5:
err4:
err3:
clk_disable_unprepare(s3c_ac97.ac97_clk);
- clk_put(s3c_ac97.ac97_clk);
err2:
- iounmap(s3c_ac97.regs);
-err1:
- release_mem_region(mem_res->start, resource_size(mem_res));
-
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
static int s3c_ac97_remove(struct platform_device *pdev)
{
- struct resource *mem_res, *irq_res;
+ struct resource *irq_res;
asoc_dma_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
@@ -501,13 +493,7 @@ static int s3c_ac97_remove(struct platform_device *pdev)
free_irq(irq_res->start, NULL);
clk_disable_unprepare(s3c_ac97.ac97_clk);
- clk_put(s3c_ac97.ac97_clk);
-
- iounmap(s3c_ac97.regs);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem_res)
- release_mem_region(mem_res->start, resource_size(mem_res));
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index ceed466af9ff..29e246803626 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -350,8 +350,16 @@ static struct snd_soc_codec_conf bells_codec_conf[] = {
},
};
+static struct snd_soc_dapm_widget bells_widgets[] = {
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
static struct snd_soc_dapm_route bells_routes[] = {
{ "Sub CLK_SYS", NULL, "OPCLK" },
+
+ { "DMIC", NULL, "MICBIAS2" },
+ { "IN2L", NULL, "DMIC" },
+ { "IN2R", NULL, "DMIC" },
};
static struct snd_soc_card bells_cards[] = {
@@ -365,6 +373,8 @@ static struct snd_soc_card bells_cards[] = {
.late_probe = bells_late_probe,
+ .dapm_widgets = bells_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
.dapm_routes = bells_routes,
.num_dapm_routes = ARRAY_SIZE(bells_routes),
@@ -383,6 +393,8 @@ static struct snd_soc_card bells_cards[] = {
.late_probe = bells_late_probe,
+ .dapm_widgets = bells_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
.dapm_routes = bells_routes,
.num_dapm_routes = ARRAY_SIZE(bells_routes),
@@ -401,6 +413,8 @@ static struct snd_soc_card bells_cards[] = {
.late_probe = bells_late_probe,
+ .dapm_widgets = bells_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
.dapm_routes = bells_routes,
.num_dapm_routes = ARRAY_SIZE(bells_routes),
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index 6e5fed30aa27..ce1e1e16f250 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -257,7 +257,6 @@ static int idma_mmap(struct snd_pcm_substream *substream,
/* From snd_pcm_lib_mmap_iomem */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_flags |= VM_IO;
size = vma->vm_end - vma->vm_start;
offset = vma->vm_pgoff << PAGE_SHIFT;
ret = io_remap_pfn_range(vma, vma->vm_start,
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
index e591c386917a..807db417d234 100644
--- a/sound/soc/samsung/neo1973_wm8753.c
+++ b/sound/soc/samsung/neo1973_wm8753.c
@@ -373,7 +373,7 @@ static struct snd_soc_dai_link neo1973_dai[] = {
{ /* Voice via BT */
.name = "Bluetooth",
.stream_name = "Voice",
- .cpu_dai_name = "dfbmcs320-pcm",
+ .cpu_dai_name = "bt-sco-pcm",
.codec_dai_name = "wm8753-voice",
.codec_name = "wm8753.0-001a",
.ops = &neo1973_voice_ops,
diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c
index e43bd4294f99..23a9204b106d 100644
--- a/sound/soc/samsung/smdk_wm8580pcm.c
+++ b/sound/soc/samsung/smdk_wm8580pcm.c
@@ -176,7 +176,6 @@ static int snd_smdk_probe(struct platform_device *pdev)
static int snd_smdk_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&smdk_pcm);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c
index 3688a32000a2..0c84ca099612 100644
--- a/sound/soc/samsung/smdk_wm8994pcm.c
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -146,7 +146,6 @@ static int snd_smdk_probe(struct platform_device *pdev)
static int snd_smdk_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&smdk_pcm);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index f830c41f97dd..30390260bb67 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -276,7 +276,7 @@ struct fsi_stream_handler {
int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev);
int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
- void (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
+ int (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
int enable);
};
#define fsi_stream_handler_call(io, func, args...) \
@@ -1188,7 +1188,7 @@ static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io)
samples);
}
-static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+static int fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
int enable)
{
struct fsi_master *master = fsi_get_master(fsi);
@@ -1201,6 +1201,8 @@ static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
if (fsi_is_clk_master(fsi))
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+
+ return 0;
}
static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io)
@@ -1409,7 +1411,7 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
return 0;
}
-static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
int start)
{
struct fsi_master *master = fsi_get_master(fsi);
@@ -1422,6 +1424,8 @@ static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
if (fsi_is_clk_master(fsi))
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+
+ return 0;
}
static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index af19f77b7bf0..0af2e4dfd139 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -227,13 +227,12 @@ static void hac_ac97_coldrst(struct snd_ac97 *ac97)
hac_ac97_warmrst(ac97);
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops hac_ac97_ops = {
.read = hac_ac97_read,
.write = hac_ac97_write,
.reset = hac_ac97_coldrst,
.warm_reset = hac_ac97_warmrst,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int hac_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -316,6 +315,10 @@ static const struct snd_soc_component_driver sh4_hac_component = {
static int hac_soc_platform_probe(struct platform_device *pdev)
{
+ ret = snd_soc_set_ac97_ops(&hac_ac97_ops);
+ if (ret != 0)
+ return ret;
+
return snd_soc_register_component(&pdev->dev, &sh4_hac_component,
sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
}
@@ -323,6 +326,7 @@ static int hac_soc_platform_probe(struct platform_device *pdev)
static int hac_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d56bbea6e75e..0ec070cf7231 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -272,8 +272,8 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
codec->debugfs_codec_root = debugfs_create_dir(codec->name,
debugfs_card_root);
if (!codec->debugfs_codec_root) {
- dev_warn(codec->dev, "ASoC: Failed to create codec debugfs"
- " directory\n");
+ dev_warn(codec->dev,
+ "ASoC: Failed to create codec debugfs directory\n");
return;
}
@@ -286,8 +286,8 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
codec->debugfs_codec_root,
codec, &codec_reg_fops);
if (!codec->debugfs_reg)
- dev_warn(codec->dev, "ASoC: Failed to create codec register"
- " debugfs file\n");
+ dev_warn(codec->dev,
+ "ASoC: Failed to create codec register debugfs file\n");
snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
}
@@ -631,8 +631,7 @@ int snd_soc_suspend(struct device *dev)
*/
if (codec->dapm.idle_bias_off) {
dev_dbg(codec->dev,
- "ASoC: idle_bias_off CODEC on"
- " over suspend\n");
+ "ASoC: idle_bias_off CODEC on over suspend\n");
break;
}
case SND_SOC_BIAS_OFF:
@@ -643,8 +642,8 @@ int snd_soc_suspend(struct device *dev)
regcache_mark_dirty(codec->control_data);
break;
default:
- dev_dbg(codec->dev, "ASoC: CODEC is on"
- " over suspend\n");
+ dev_dbg(codec->dev,
+ "ASoC: CODEC is on over suspend\n");
break;
}
}
@@ -713,8 +712,8 @@ static void soc_resume_deferred(struct work_struct *work)
codec->suspended = 0;
break;
default:
- dev_dbg(codec->dev, "ASoC: CODEC was on over"
- " suspend\n");
+ dev_dbg(codec->dev,
+ "ASoC: CODEC was on over suspend\n");
break;
}
}
@@ -1110,8 +1109,8 @@ static int soc_probe_codec(struct snd_soc_card *card,
}
WARN(codec->dapm.idle_bias_off &&
codec->dapm.bias_level != SND_SOC_BIAS_OFF,
- "codec %s can not start from non-off bias"
- " with idle_bias_off==1\n", codec->name);
+ "codec %s can not start from non-off bias with idle_bias_off==1\n",
+ codec->name);
}
/* If the driver didn't set I/O up try regmap */
@@ -1582,8 +1581,9 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec,
codec->compress_type = compress_type;
ret = snd_soc_cache_init(codec);
if (ret < 0) {
- dev_err(codec->dev, "ASoC: Failed to set cache compression"
- " type: %d\n", ret);
+ dev_err(codec->dev,
+ "ASoC: Failed to set cache compression type: %d\n",
+ ret);
return ret;
}
codec->cache_init = 1;
@@ -1639,8 +1639,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
- dev_err(card->dev, "ASoC: can't create sound card for"
- " card %s: %d\n", card->name, ret);
+ dev_err(card->dev,
+ "ASoC: can't create sound card for card %s: %d\n",
+ card->name, ret);
goto base_error;
}
card->snd_card->dev = card->dev;
@@ -1815,8 +1816,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
for (i = 0; i < card->num_rtd; i++) {
ret = soc_register_ac97_dai_link(&card->rtd[i]);
if (ret < 0) {
- dev_err(card->dev, "ASoC: failed to register AC97:"
- " %d\n", ret);
+ dev_err(card->dev,
+ "ASoC: failed to register AC97: %d\n", ret);
while (--i >= 0)
soc_unregister_ac97_dai_link(card->rtd[i].codec);
goto probe_aux_dev_err;
@@ -2079,6 +2080,23 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+ if (ops == soc_ac97_ops)
+ return 0;
+
+ if (soc_ac97_ops && ops)
+ return -EBUSY;
+
+ soc_ac97_ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
/**
* snd_soc_free_ac97_codec - free AC97 codec device
* @codec: audio codec
@@ -2219,29 +2237,6 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
EXPORT_SYMBOL_GPL(snd_soc_test_bits);
/**
- * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
- * @substream: the pcm substream
- * @hw: the hardware parameters
- *
- * Sets the substream runtime hardware parameters.
- */
-int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
- const struct snd_pcm_hardware *hw)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.info = hw->info;
- runtime->hw.formats = hw->formats;
- runtime->hw.period_bytes_min = hw->period_bytes_min;
- runtime->hw.period_bytes_max = hw->period_bytes_max;
- runtime->hw.periods_min = hw->periods_min;
- runtime->hw.periods_max = hw->periods_max;
- runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
- runtime->hw.fifo_size = hw->fifo_size;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
-
-/**
* snd_soc_cnew - create new control
* @_template: control template
* @data: control private data
@@ -2259,7 +2254,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
struct snd_kcontrol_new template;
struct snd_kcontrol *kcontrol;
char *name = NULL;
- int name_len;
memcpy(&template, _template, sizeof(template));
template.index = 0;
@@ -2268,13 +2262,10 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
long_name = template.name;
if (prefix) {
- name_len = strlen(long_name) + strlen(prefix) + 2;
- name = kmalloc(name_len, GFP_KERNEL);
+ name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name);
if (!name)
return NULL;
- snprintf(name, name_len, "%s %s", prefix, long_name);
-
template.name = name;
} else {
template.name = long_name;
@@ -3586,14 +3577,16 @@ int snd_soc_register_card(struct snd_soc_card *card)
* not both or neither.
*/
if (!!link->codec_name == !!link->codec_of_node) {
- dev_err(card->dev, "ASoC: Neither/both codec"
- " name/of_node are set for %s\n", link->name);
+ dev_err(card->dev,
+ "ASoC: Neither/both codec name/of_node are set for %s\n",
+ link->name);
return -EINVAL;
}
/* Codec DAI name must be specified */
if (!link->codec_dai_name) {
- dev_err(card->dev, "ASoC: codec_dai_name not"
- " set for %s\n", link->name);
+ dev_err(card->dev,
+ "ASoC: codec_dai_name not set for %s\n",
+ link->name);
return -EINVAL;
}
@@ -3602,8 +3595,9 @@ int snd_soc_register_card(struct snd_soc_card *card)
* can be left unspecified, and a dummy platform will be used.
*/
if (link->platform_name && link->platform_of_node) {
- dev_err(card->dev, "ASoC: Both platform name/of_node"
- " are set for %s\n", link->name);
+ dev_err(card->dev,
+ "ASoC: Both platform name/of_node are set for %s\n",
+ link->name);
return -EINVAL;
}
@@ -3613,8 +3607,9 @@ int snd_soc_register_card(struct snd_soc_card *card)
* name alone..
*/
if (link->cpu_name && link->cpu_of_node) {
- dev_err(card->dev, "ASoC: Neither/both "
- "cpu name/of_node are set for %s\n",link->name);
+ dev_err(card->dev,
+ "ASoC: Neither/both cpu name/of_node are set for %s\n",
+ link->name);
return -EINVAL;
}
/*
@@ -3623,8 +3618,9 @@ int snd_soc_register_card(struct snd_soc_card *card)
*/
if (!link->cpu_dai_name &&
!(link->cpu_name || link->cpu_of_node)) {
- dev_err(card->dev, "ASoC: Neither cpu_dai_name nor "
- "cpu_name/of_node are set for %s\n", link->name);
+ dev_err(card->dev,
+ "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
+ link->name);
return -EINVAL;
}
}
@@ -3728,8 +3724,9 @@ static inline char *fmt_multiple_name(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
if (dai_drv->name == NULL) {
- dev_err(dev, "ASoC: error - multiple DAI %s registered with"
- " no name\n", dev_name(dev));
+ dev_err(dev,
+ "ASoC: error - multiple DAI %s registered with no name\n",
+ dev_name(dev));
return NULL;
}
@@ -3859,8 +3856,9 @@ static int snd_soc_register_dais(struct device *dev,
list_for_each_entry(codec, &codec_list, list) {
if (codec->dev == dev) {
- dev_dbg(dev, "ASoC: Mapped DAI %s to "
- "CODEC %s\n", dai->name, codec->name);
+ dev_dbg(dev,
+ "ASoC: Mapped DAI %s to CODEC %s\n",
+ dai->name, codec->name);
dai->codec = codec;
break;
}
@@ -4296,8 +4294,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
num_routes = of_property_count_strings(np, propname);
if (num_routes < 0 || num_routes & 1) {
- dev_err(card->dev, "ASoC: Property '%s' does not exist or its"
- " length is not even\n", propname);
+ dev_err(card->dev,
+ "ASoC: Property '%s' does not exist or its length is not even\n",
+ propname);
return -EINVAL;
}
num_routes /= 2;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c7051c457b75..b94190820e8c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -64,6 +64,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_virt_mux] = 5,
[snd_soc_dapm_value_mux] = 5,
[snd_soc_dapm_dac] = 6,
+ [snd_soc_dapm_switch] = 7,
[snd_soc_dapm_mixer] = 7,
[snd_soc_dapm_mixer_named_ctl] = 7,
[snd_soc_dapm_pga] = 8,
@@ -83,6 +84,7 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_line] = 2,
[snd_soc_dapm_out_drv] = 2,
[snd_soc_dapm_pga] = 4,
+ [snd_soc_dapm_switch] = 5,
[snd_soc_dapm_mixer_named_ctl] = 5,
[snd_soc_dapm_mixer] = 5,
[snd_soc_dapm_dac] = 6,
@@ -365,11 +367,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
val = soc_widget_read(w, e->reg);
item = (val >> e->shift_l) & e->mask;
- p->connect = 0;
- for (i = 0; i < e->max; i++) {
- if (!(strcmp(p->name, e->texts[i])) && item == i)
- p->connect = 1;
- }
+ if (item < e->max && !strcmp(p->name, e->texts[item]))
+ p->connect = 1;
+ else
+ p->connect = 0;
}
break;
case snd_soc_dapm_virt_mux: {
@@ -399,11 +400,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
break;
}
- p->connect = 0;
- for (i = 0; i < e->max; i++) {
- if (!(strcmp(p->name, e->texts[i])) && item == i)
- p->connect = 1;
- }
+ if (item < e->max && !strcmp(p->name, e->texts[item]))
+ p->connect = 1;
+ else
+ p->connect = 0;
}
break;
/* does not affect routing - always connected */
@@ -507,6 +507,11 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
return 0;
}
+static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
+{
+ kfree(kctl->private_data);
+}
+
/*
* Determine if a kcontrol is shared. If it is, look it up. If it isn't,
* create it. Either way, add the widget into the control's widget list
@@ -524,7 +529,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
int wlistentries;
size_t wlistsize;
bool wname_in_long_name, kcname_in_long_name;
- size_t name_len;
char *long_name;
const char *name;
int ret;
@@ -589,25 +593,19 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
}
if (wname_in_long_name && kcname_in_long_name) {
- name_len = strlen(w->name) - prefix_len + 1 +
- strlen(w->kcontrol_news[kci].name) + 1;
-
- long_name = kmalloc(name_len, GFP_KERNEL);
- if (long_name == NULL) {
- kfree(wlist);
- return -ENOMEM;
- }
-
/*
* The control will get a prefix from the control
* creation process but we're also using the same
* prefix for widgets so cut the prefix off the
* front of the widget name.
*/
- snprintf(long_name, name_len, "%s %s",
+ long_name = kasprintf(GFP_KERNEL, "%s %s",
w->name + prefix_len,
w->kcontrol_news[kci].name);
- long_name[name_len - 1] = '\0';
+ if (long_name == NULL) {
+ kfree(wlist);
+ return -ENOMEM;
+ }
name = long_name;
} else if (wname_in_long_name) {
@@ -620,17 +618,16 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
prefix);
+ kcontrol->private_free = dapm_kcontrol_free;
+ kfree(long_name);
ret = snd_ctl_add(card, kcontrol);
if (ret < 0) {
dev_err(dapm->dev,
"ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
w->name, name, ret);
kfree(wlist);
- kfree(long_name);
return ret;
}
-
- path->long_name = long_name;
}
kcontrol->private_data = wlist;
@@ -1270,6 +1267,14 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
ev_name = "POST_PMD";
power = 0;
break;
+ case SND_SOC_DAPM_WILL_PMU:
+ ev_name = "WILL_PMU";
+ power = 1;
+ break;
+ case SND_SOC_DAPM_WILL_PMD:
+ ev_name = "WILL_PMD";
+ power = 0;
+ break;
default:
BUG();
return;
@@ -1730,6 +1735,14 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
&async_domain);
async_synchronize_full_domain(&async_domain);
+ list_for_each_entry(w, &down_list, power_list) {
+ dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD);
+ }
+
+ list_for_each_entry(w, &up_list, power_list) {
+ dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU);
+ }
+
/* Power down widgets first; try to avoid amplifying pops. */
dapm_seq_run(dapm, &down_list, event, false);
@@ -2094,6 +2107,14 @@ static void snd_soc_dapm_sys_remove(struct device *dev)
device_remove_file(dev, &dev_attr_dapm_widget);
}
+static void dapm_free_path(struct snd_soc_dapm_path *path)
+{
+ list_del(&path->list_sink);
+ list_del(&path->list_source);
+ list_del(&path->list);
+ kfree(path);
+}
+
/* free all dapm widgets and resources */
static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
{
@@ -2109,20 +2130,12 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
* While removing the path, remove reference to it from both
* source and sink widgets so that path is removed only once.
*/
- list_for_each_entry_safe(p, next_p, &w->sources, list_sink) {
- list_del(&p->list_sink);
- list_del(&p->list_source);
- list_del(&p->list);
- kfree(p->long_name);
- kfree(p);
- }
- list_for_each_entry_safe(p, next_p, &w->sinks, list_source) {
- list_del(&p->list_sink);
- list_del(&p->list_source);
- list_del(&p->list);
- kfree(p->long_name);
- kfree(p);
- }
+ list_for_each_entry_safe(p, next_p, &w->sources, list_sink)
+ dapm_free_path(p);
+
+ list_for_each_entry_safe(p, next_p, &w->sinks, list_source)
+ dapm_free_path(p);
+
kfree(w->kcontrols);
kfree(w->name);
kfree(w);
@@ -2398,10 +2411,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
dapm_mark_dirty(path->source, "Route removed");
dapm_mark_dirty(path->sink, "Route removed");
- list_del(&path->list);
- list_del(&path->list_sink);
- list_del(&path->list_source);
- kfree(path);
+ dapm_free_path(path);
} else {
dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
source, sink);
@@ -3055,7 +3065,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
- size_t name_len;
int ret;
if ((w = dapm_cnew_widget(widget)) == NULL)
@@ -3096,19 +3105,16 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
break;
}
- name_len = strlen(widget->name) + 1;
if (dapm->codec && dapm->codec->name_prefix)
- name_len += 1 + strlen(dapm->codec->name_prefix);
- w->name = kmalloc(name_len, GFP_KERNEL);
+ w->name = kasprintf(GFP_KERNEL, "%s %s",
+ dapm->codec->name_prefix, widget->name);
+ else
+ w->name = kasprintf(GFP_KERNEL, "%s", widget->name);
+
if (w->name == NULL) {
kfree(w);
return NULL;
}
- if (dapm->codec && dapm->codec->name_prefix)
- snprintf((char *)w->name, name_len, "%s %s",
- dapm->codec->name_prefix, widget->name);
- else
- snprintf((char *)w->name, name_len, "%s", widget->name);
switch (w->id) {
case snd_soc_dapm_switch:
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index ccb6be4d658d..b6c640332a17 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -33,6 +33,29 @@
#define DPCM_MAX_BE_USERS 8
+/**
+ * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
+ * @substream: the pcm substream
+ * @hw: the hardware parameters
+ *
+ * Sets the substream runtime hardware parameters.
+ */
+int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
+ const struct snd_pcm_hardware *hw)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->hw.info = hw->info;
+ runtime->hw.formats = hw->formats;
+ runtime->hw.period_bytes_min = hw->period_bytes_min;
+ runtime->hw.period_bytes_max = hw->period_bytes_max;
+ runtime->hw.periods_min = hw->periods_min;
+ runtime->hw.periods_max = hw->periods_max;
+ runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
+ runtime->hw.fifo_size = hw->fifo_size;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
+
/* DPCM stream event, send event to FE and all active BEs. */
static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
int event)
@@ -124,6 +147,26 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
}
}
+static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw,
+ struct snd_soc_pcm_stream *codec_stream,
+ struct snd_soc_pcm_stream *cpu_stream)
+{
+ hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min);
+ hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max);
+ hw->channels_min = max(codec_stream->channels_min,
+ cpu_stream->channels_min);
+ hw->channels_max = min(codec_stream->channels_max,
+ cpu_stream->channels_max);
+ hw->formats = codec_stream->formats & cpu_stream->formats;
+ hw->rates = codec_stream->rates & cpu_stream->rates;
+ if (codec_stream->rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ hw->rates |= cpu_stream->rates;
+ if (cpu_stream->rates
+ & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+ hw->rates |= codec_stream->rates;
+}
+
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
@@ -189,51 +232,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw.rate_min =
- max(codec_dai_drv->playback.rate_min,
- cpu_dai_drv->playback.rate_min);
- runtime->hw.rate_max =
- min(codec_dai_drv->playback.rate_max,
- cpu_dai_drv->playback.rate_max);
- runtime->hw.channels_min =
- max(codec_dai_drv->playback.channels_min,
- cpu_dai_drv->playback.channels_min);
- runtime->hw.channels_max =
- min(codec_dai_drv->playback.channels_max,
- cpu_dai_drv->playback.channels_max);
- runtime->hw.formats =
- codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
- runtime->hw.rates =
- codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
- if (codec_dai_drv->playback.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= cpu_dai_drv->playback.rates;
- if (cpu_dai_drv->playback.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= codec_dai_drv->playback.rates;
+ soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback,
+ &cpu_dai_drv->playback);
} else {
- runtime->hw.rate_min =
- max(codec_dai_drv->capture.rate_min,
- cpu_dai_drv->capture.rate_min);
- runtime->hw.rate_max =
- min(codec_dai_drv->capture.rate_max,
- cpu_dai_drv->capture.rate_max);
- runtime->hw.channels_min =
- max(codec_dai_drv->capture.channels_min,
- cpu_dai_drv->capture.channels_min);
- runtime->hw.channels_max =
- min(codec_dai_drv->capture.channels_max,
- cpu_dai_drv->capture.channels_max);
- runtime->hw.formats =
- codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
- runtime->hw.rates =
- codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
- if (codec_dai_drv->capture.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= cpu_dai_drv->capture.rates;
- if (cpu_dai_drv->capture.rates
- & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
- runtime->hw.rates |= codec_dai_drv->capture.rates;
+ soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture,
+ &cpu_dai_drv->capture);
}
ret = -EINVAL;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 4b3be6c3c91e..29b211e9c060 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -159,15 +159,10 @@ int __init snd_soc_util_init(void)
{
int ret;
- soc_dummy_dev = platform_device_alloc("snd-soc-dummy", -1);
- if (!soc_dummy_dev)
- return -ENOMEM;
-
- ret = platform_device_add(soc_dummy_dev);
- if (ret != 0) {
- platform_device_put(soc_dummy_dev);
- return ret;
- }
+ soc_dummy_dev =
+ platform_device_register_simple("snd-soc-dummy", -1, NULL, 0);
+ if (IS_ERR(soc_dummy_dev))
+ return PTR_ERR(soc_dummy_dev);
ret = platform_driver_register(&soc_dummy_driver);
if (ret != 0)
diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig
new file mode 100644
index 000000000000..3567d73b218e
--- /dev/null
+++ b/sound/soc/spear/Kconfig
@@ -0,0 +1,9 @@
+config SND_SPEAR_SOC
+ tristate
+ select SND_SOC_DMAENGINE_PCM
+
+config SND_SPEAR_SPDIF_OUT
+ tristate
+
+config SND_SPEAR_SPDIF_IN
+ tristate
diff --git a/sound/soc/spear/Makefile b/sound/soc/spear/Makefile
new file mode 100644
index 000000000000..c4ea7161056c
--- /dev/null
+++ b/sound/soc/spear/Makefile
@@ -0,0 +1,8 @@
+# SPEAR Platform Support
+snd-soc-spear-pcm-objs := spear_pcm.o
+snd-soc-spear-spdif-in-objs := spdif_in.o
+snd-soc-spear-spdif-out-objs := spdif_out.o
+
+obj-$(CONFIG_SND_SPEAR_SOC) += snd-soc-spear-pcm.o
+obj-$(CONFIG_SND_SPEAR_SPDIF_IN) += snd-soc-spear-spdif-in.o
+obj-$(CONFIG_SND_SPEAR_SPDIF_OUT) += snd-soc-spear-spdif-out.o
diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c
index 14d57e89bcba..63acfeb4b69d 100644
--- a/sound/soc/spear/spdif_in.c
+++ b/sound/soc/spear/spdif_in.c
@@ -49,15 +49,12 @@ static void spdif_in_configure(struct spdif_in_dev *host)
writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK);
}
-static int spdif_in_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
+static int spdif_in_dai_probe(struct snd_soc_dai *dai)
{
- struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
- if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
- return -EINVAL;
+ dai->capture_dma_data = &host->dma_params;
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params);
return 0;
}
@@ -70,7 +67,6 @@ static void spdif_in_shutdown(struct snd_pcm_substream *substream,
return;
writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK);
- snd_soc_dai_set_dma_data(dai, substream, NULL);
}
static void spdif_in_format(struct spdif_in_dev *host, u32 format)
@@ -151,13 +147,13 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
}
static struct snd_soc_dai_ops spdif_in_dai_ops = {
- .startup = spdif_in_startup,
.shutdown = spdif_in_shutdown,
.trigger = spdif_in_trigger,
.hw_params = spdif_in_hw_params,
};
-struct snd_soc_dai_driver spdif_in_dai = {
+static struct snd_soc_dai_driver spdif_in_dai = {
+ .probe = spdif_in_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
@@ -235,7 +231,7 @@ static int spdif_in_probe(struct platform_device *pdev)
if (host->irq < 0)
return -EINVAL;
- host->clk = clk_get(&pdev->dev, NULL);
+ host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk))
return PTR_ERR(host->clk);
@@ -257,34 +253,21 @@ static int spdif_in_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0,
"spdif-in", host);
if (ret) {
- clk_put(host->clk);
dev_warn(&pdev->dev, "request_irq failed\n");
return ret;
}
- ret = snd_soc_register_component(&pdev->dev, &spdif_in_component,
+ return snd_soc_register_component(&pdev->dev, &spdif_in_component,
&spdif_in_dai, 1);
- if (ret != 0) {
- clk_put(host->clk);
- return ret;
- }
-
- return 0;
}
static int spdif_in_remove(struct platform_device *pdev)
{
- struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev);
-
snd_soc_unregister_component(&pdev->dev);
- dev_set_drvdata(&pdev->dev, NULL);
-
- clk_put(host->clk);
return 0;
}
-
static struct platform_driver spdif_in_driver = {
.probe = spdif_in_probe,
.remove = spdif_in_remove,
diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c
index 1e3c3dda3598..2fdf68c98d22 100644
--- a/sound/soc/spear/spdif_out.c
+++ b/sound/soc/spear/spdif_out.c
@@ -62,8 +62,6 @@ static int spdif_out_startup(struct snd_pcm_substream *substream,
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return -EINVAL;
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params);
-
ret = clk_enable(host->clk);
if (ret)
return ret;
@@ -84,7 +82,6 @@ static void spdif_out_shutdown(struct snd_pcm_substream *substream,
clk_disable(host->clk);
host->running = false;
- snd_soc_dai_set_dma_data(dai, substream, NULL);
}
static void spdif_out_clock(struct spdif_out_dev *host, u32 core_freq,
@@ -243,8 +240,12 @@ static const struct snd_kcontrol_new spdif_out_controls[] = {
spdif_mute_get, spdif_mute_put),
};
-int spdif_soc_dai_probe(struct snd_soc_dai *dai)
+static int spdif_soc_dai_probe(struct snd_soc_dai *dai)
{
+ struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &host->dma_params;
+
return snd_soc_add_dai_controls(dai, spdif_out_controls,
ARRAY_SIZE(spdif_out_controls));
}
@@ -281,30 +282,18 @@ static int spdif_out_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name)) {
- dev_warn(&pdev->dev, "Failed to get memory resourse\n");
- return -ENOENT;
- }
-
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) {
dev_warn(&pdev->dev, "kzalloc fail\n");
return -ENOMEM;
}
- host->io_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!host->io_base) {
- dev_warn(&pdev->dev, "ioremap failed\n");
- return -ENOMEM;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->io_base))
+ return PTR_ERR(host->io_base);
- host->clk = clk_get(&pdev->dev, NULL);
+ host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk))
return PTR_ERR(host->clk);
@@ -320,22 +309,12 @@ static int spdif_out_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &spdif_out_component,
&spdif_out_dai, 1);
- if (ret != 0) {
- clk_put(host->clk);
- return ret;
- }
-
- return 0;
+ return ret;
}
static int spdif_out_remove(struct platform_device *pdev)
{
- struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
-
snd_soc_unregister_component(&pdev->dev);
- dev_set_drvdata(&pdev->dev, NULL);
-
- clk_put(host->clk);
return 0;
}
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c
index 2fbd4899d8ef..4707f2b862c3 100644
--- a/sound/soc/spear/spear_pcm.c
+++ b/sound/soc/spear/spear_pcm.c
@@ -13,19 +13,13 @@
#include <linux/module.h>
#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/scatterlist.h>
-#include <linux/slab.h>
-#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/spear_dma.h>
-static struct snd_pcm_hardware spear_pcm_hardware = {
+static const struct snd_pcm_hardware spear_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
@@ -37,149 +31,33 @@ static struct snd_pcm_hardware spear_pcm_hardware = {
.fifo_size = 0, /* fifo size in bytes */
};
-static int spear_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static struct dma_chan *spear_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_substream *substream)
{
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ struct spear_dma_data *dma_data;
- return 0;
-}
-
-static int spear_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
-
- return 0;
-}
-
-static int spear_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- struct spear_dma_data *dma_data = (struct spear_dma_data *)
- snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- int ret;
-
- ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware);
- if (ret)
- return ret;
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter,
- dma_data);
+ return snd_dmaengine_pcm_request_channel(dma_data->filter, dma_data);
}
-static int spear_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area, runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops spear_pcm_ops = {
- .open = spear_pcm_open,
- .close = snd_dmaengine_pcm_close_release_chan,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = spear_pcm_hw_params,
- .hw_free = spear_pcm_hw_free,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer,
- .mmap = spear_pcm_mmap,
-};
-
-static int
-spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
- size_t size)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
-
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
-
- dev_info(buf->dev.dev,
- " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- (void *)buf->area, (void *)buf->addr, size);
-
- buf->bytes = size;
- return 0;
-}
-
-static void spear_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf || !buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-static u64 spear_pcm_dmamask = DMA_BIT_MASK(32);
-
-static int spear_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- int ret;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &spear_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (rtd->cpu_dai->driver->playback.channels_min) {
- ret = spear_pcm_preallocate_dma_buffer(rtd->pcm,
- SNDRV_PCM_STREAM_PLAYBACK,
- spear_pcm_hardware.buffer_bytes_max);
- if (ret)
- return ret;
- }
-
- if (rtd->cpu_dai->driver->capture.channels_min) {
- ret = spear_pcm_preallocate_dma_buffer(rtd->pcm,
- SNDRV_PCM_STREAM_CAPTURE,
- spear_pcm_hardware.buffer_bytes_max);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver spear_soc_platform = {
- .ops = &spear_pcm_ops,
- .pcm_new = spear_pcm_new,
- .pcm_free = spear_pcm_free,
+static const struct snd_dmaengine_pcm_config spear_dmaengine_pcm_config = {
+ .pcm_hardware = &spear_pcm_hardware,
+ .compat_request_channel = spear_pcm_request_chan,
+ .prealloc_buffer_size = 16 * 1024,
};
static int spear_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &spear_soc_platform);
+ return snd_dmaengine_pcm_register(&pdev->dev,
+ &spear_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_NO_DT |
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static int spear_soc_platform_remove(struct platform_device *pdev)
{
- snd_soc_unregister_platform(&pdev->dev);
-
+ snd_dmaengine_pcm_unregister(&pdev->dev);
return 0;
}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index b1c9d573da05..995b120c2cd0 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -59,6 +59,16 @@ config SND_SOC_TEGRA30_I2S
Tegra30 I2S interface. You will also need to select the individual
machine drivers to support below.
+config SND_SOC_TEGRA_RT5640
+ tristate "SoC Audio support for Tegra boards using an RT5640 codec"
+ depends on SND_SOC_TEGRA && I2C
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+ select SND_SOC_RT5640
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the RT5640 codec, such as Dalmore.
+
config SND_SOC_TEGRA_WM8753
tristate "SoC Audio support for Tegra boards using a WM8753 codec"
depends on SND_SOC_TEGRA && I2C
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 416a14bde41b..21d2550a08a4 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -18,12 +18,14 @@ obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
# Tegra machine Support
+snd-soc-tegra-rt5640-objs := tegra_rt5640.o
snd-soc-tegra-wm8753-objs := tegra_wm8753.o
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-wm9712-objs := tegra_wm9712.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
+obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 2f70ea7f6618..e58233f7df61 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -142,13 +142,12 @@ static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
} while (!time_after(jiffies, timeout));
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops tegra20_ac97_ops = {
.read = tegra20_ac97_codec_read,
.write = tegra20_ac97_codec_write,
.reset = tegra20_ac97_codec_reset,
.warm_reset = tegra20_ac97_codec_warm_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
{
@@ -313,7 +312,7 @@ static const struct regmap_config tegra20_ac97_regmap_config = {
static int tegra20_ac97_platform_probe(struct platform_device *pdev)
{
struct tegra20_ac97 *ac97;
- struct resource *mem, *memregion;
+ struct resource *mem;
u32 of_dma[2];
void __iomem *regs;
int ret = 0;
@@ -327,7 +326,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
}
dev_set_drvdata(&pdev->dev, ac97);
- ac97->clk_ac97 = clk_get(&pdev->dev, NULL);
+ ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ac97->clk_ac97)) {
dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
ret = PTR_ERR(ac97->clk_ac97);
@@ -341,18 +340,9 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
goto err_clk_put;
}
- memregion = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem), DRV_NAME);
- if (!memregion) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err_clk_put;
- }
-
- regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
+ regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
goto err_clk_put;
}
@@ -403,23 +393,9 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
ac97->capture_dma_data.maxburst = 4;
ac97->capture_dma_data.slave_id = of_dma[0];
- ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
- &tegra20_ac97_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
- ret = -ENOMEM;
- goto err_clk_put;
- }
-
- ret = tegra_pcm_platform_register(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_component;
- }
-
ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
if (ret)
- goto err_unregister_pcm;
+ goto err_clk_put;
ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data);
if (ret)
@@ -431,20 +407,40 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
goto err_asoc_utils_fini;
}
+ ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
+ goto err_asoc_utils_fini;
+ }
+
+ ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
+ &tegra20_ac97_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_asoc_utils_fini;
+ }
+
+ ret = tegra_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_component;
+ }
+
/* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
workdata = ac97;
return 0;
-err_asoc_utils_fini:
- tegra_asoc_utils_fini(&ac97->util_data);
err_unregister_pcm:
tegra_pcm_platform_unregister(&pdev->dev);
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
+err_asoc_utils_fini:
+ tegra_asoc_utils_fini(&ac97->util_data);
err_clk_put:
- clk_put(ac97->clk_ac97);
err:
+ snd_soc_set_ac97_ops(NULL);
return ret;
}
@@ -458,7 +454,8 @@ static int tegra20_ac97_platform_remove(struct platform_device *pdev)
tegra_asoc_utils_fini(&ac97->util_data);
clk_disable_unprepare(ac97->clk_ac97);
- clk_put(ac97->clk_ac97);
+
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 23e592f453fa..d554d46d08b5 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -627,9 +627,34 @@ static int tegra30_ahub_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int tegra30_ahub_suspend(struct device *dev)
+{
+ regcache_mark_dirty(ahub->regmap_ahub);
+ regcache_mark_dirty(ahub->regmap_apbif);
+
+ return 0;
+}
+
+static int tegra30_ahub_resume(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return ret;
+ ret = regcache_sync(ahub->regmap_ahub);
+ ret |= regcache_sync(ahub->regmap_apbif);
+ pm_runtime_put(dev);
+
+ return ret;
+}
+#endif
+
static const struct dev_pm_ops tegra30_ahub_pm_ops = {
SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
tegra30_ahub_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra30_ahub_suspend, tegra30_ahub_resume)
};
static struct platform_driver tegra30_ahub_driver = {
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 31d092d83c71..d04146cad61f 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -514,6 +514,31 @@ static int tegra30_i2s_platform_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int tegra30_i2s_suspend(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(i2s->regmap);
+
+ return 0;
+}
+
+static int tegra30_i2s_resume(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return ret;
+ ret = regcache_sync(i2s->regmap);
+ pm_runtime_put(dev);
+
+ return ret;
+}
+#endif
+
static const struct of_device_id tegra30_i2s_of_match[] = {
{ .compatible = "nvidia,tegra30-i2s", },
{},
@@ -522,6 +547,7 @@ static const struct of_device_id tegra30_i2s_of_match[] = {
static const struct dev_pm_ops tegra30_i2s_pm_ops = {
SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
tegra30_i2s_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra30_i2s_suspend, tegra30_i2s_resume)
};
static struct platform_driver tegra30_i2s_driver = {
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index 24fb001be7f4..d173880f290d 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -173,7 +173,6 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev)
{
int ret;
- bool new_clocks = false;
data->dev = dev;
@@ -181,40 +180,28 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
else if (of_machine_is_compatible("nvidia,tegra30"))
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
- else if (of_machine_is_compatible("nvidia,tegra114")) {
+ else if (of_machine_is_compatible("nvidia,tegra114"))
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
- new_clocks = true;
- } else {
+ else {
dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
return -EINVAL;
}
- if (new_clocks)
- data->clk_pll_a = clk_get(dev, "pll_a");
- else
- data->clk_pll_a = clk_get_sys(NULL, "pll_a");
+ data->clk_pll_a = clk_get(dev, "pll_a");
if (IS_ERR(data->clk_pll_a)) {
dev_err(data->dev, "Can't retrieve clk pll_a\n");
ret = PTR_ERR(data->clk_pll_a);
goto err;
}
- if (new_clocks)
- data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
- else
- data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
if (IS_ERR(data->clk_pll_a_out0)) {
dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
ret = PTR_ERR(data->clk_pll_a_out0);
goto err_put_pll_a;
}
- if (new_clocks)
- data->clk_cdev1 = clk_get(dev, "mclk");
- else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
- data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
- else
- data->clk_cdev1 = clk_get_sys("extern1", NULL);
+ data->clk_cdev1 = clk_get(dev, "mclk");
if (IS_ERR(data->clk_cdev1)) {
dev_err(data->dev, "Can't retrieve clk cdev1\n");
ret = PTR_ERR(data->clk_cdev1);
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
new file mode 100644
index 000000000000..08794f915a94
--- /dev/null
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -0,0 +1,257 @@
+/*
+* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/rt5640.h"
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-rt5640"
+
+struct tegra_rt5640 {
+ struct tegra_asoc_utils_data util_data;
+ int gpio_hp_det;
+};
+
+static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ mclk = 256 * srate;
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_rt5640_ops = {
+ .hw_params = tegra_rt5640_asoc_hw_params,
+};
+
+static struct snd_soc_jack tegra_rt5640_hp_jack;
+
+static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
+ .name = "Headphone detection",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+};
+
+static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card);
+
+ snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
+ &tegra_rt5640_hp_jack);
+ snd_soc_jack_add_pins(&tegra_rt5640_hp_jack,
+ ARRAY_SIZE(tegra_rt5640_hp_jack_pins),
+ tegra_rt5640_hp_jack_pins);
+
+ if (gpio_is_valid(machine->gpio_hp_det)) {
+ tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
+ snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
+ 1,
+ &tegra_rt5640_hp_jack_gpio);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_rt5640_dai = {
+ .name = "RT5640",
+ .stream_name = "RT5640 PCM",
+ .codec_dai_name = "rt5640-aif1",
+ .init = tegra_rt5640_asoc_init,
+ .ops = &tegra_rt5640_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_rt5640 = {
+ .name = "tegra-rt5640",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_rt5640_dai,
+ .num_links = 1,
+ .controls = tegra_rt5640_controls,
+ .num_controls = ARRAY_SIZE(tegra_rt5640_controls),
+ .dapm_widgets = tegra_rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int tegra_rt5640_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &snd_soc_tegra_rt5640;
+ struct tegra_rt5640 *machine;
+ int ret;
+
+ machine = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_rt5640), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n");
+ return -ENOMEM;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+ if (machine->gpio_hp_det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_rt5640_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!tegra_rt5640_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_rt5640_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!tegra_rt5640_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_rt5640_dai.platform_of_node = tegra_rt5640_dai.cpu_of_node;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err:
+ return ret;
+}
+
+static int tegra_rt5640_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
+
+ snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
+ &tegra_rt5640_hp_jack_gpio);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_rt5640_of_match[] = {
+ { .compatible = "nvidia,tegra-audio-rt5640", },
+ {},
+};
+
+static struct platform_driver tegra_rt5640_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_rt5640_of_match,
+ },
+ .probe = tegra_rt5640_probe,
+ .remove = tegra_rt5640_remove,
+};
+module_platform_driver(tegra_rt5640_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match);
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 8a2840304d28..4bcce8a3cded 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -119,12 +119,11 @@ static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
}
/* AC97 controller operations */
-struct snd_ac97_bus_ops soc_ac97_ops = {
+static struct snd_ac97_bus_ops txx9aclc_ac97_ops = {
.read = txx9aclc_ac97_read,
.write = txx9aclc_ac97_write,
.reset = txx9aclc_ac97_cold_reset,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
{
@@ -188,9 +187,9 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
if (!r)
return -EBUSY;
- if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
- dev_name(&pdev->dev)))
- return -EBUSY;
+ drvdata->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(drvdata->base))
+ return PTR_ERR(drvdata->base);
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
@@ -201,14 +200,15 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
r->start >= TXX9_DIRECTMAP_BASE &&
r->start < TXX9_DIRECTMAP_BASE + 0x400000)
drvdata->physbase |= 0xf00000000ull;
- drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (!drvdata->base)
- return -EBUSY;
err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
0, dev_name(&pdev->dev), drvdata);
if (err < 0)
return err;
+ err = snd_soc_set_ac97_ops(&txx9aclc_ac97_ops);
+ if (err < 0)
+ return err;
+
return snd_soc_register_component(&pdev->dev, &txx9aclc_ac97_component,
&txx9aclc_ac97_dai, 1);
}
@@ -216,6 +216,7 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
static int txx9aclc_ac97_dev_remove(struct platform_device *pdev)
{
snd_soc_unregister_component(&pdev->dev);
+ snd_soc_set_ac97_ops(NULL);
return 0;
}
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 204b899c2311..8f5cd00a6e46 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -27,7 +27,7 @@
#include "mop500_ab8500.h"
/* Define the whole MOP500 soundcard, linking platform to the codec-drivers */
-struct snd_soc_dai_link mop500_dai_links[] = {
+static struct snd_soc_dai_link mop500_dai_links[] = {
{
.name = "ab8500_0",
.stream_name = "ab8500_0",
diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c
index 892ad9a05c9f..7e923ecf8901 100644
--- a/sound/soc/ux500/mop500_ab8500.c
+++ b/sound/soc/ux500/mop500_ab8500.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/mutex.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -24,6 +25,7 @@
#include "ux500_pcm.h"
#include "ux500_msp_dai.h"
+#include "mop500_ab8500.h"
#include "../codecs/ab8500-codec.h"
#define TX_SLOT_MONO 0x0008
@@ -43,6 +45,12 @@
static unsigned int tx_slots = DEF_TX_SLOTS;
static unsigned int rx_slots = DEF_RX_SLOTS;
+/* Configuration consistency parameters */
+static DEFINE_MUTEX(mop500_ab8500_params_lock);
+static unsigned long mop500_ab8500_usage;
+static int mop500_ab8500_rate;
+static int mop500_ab8500_channels;
+
/* Clocks */
static const char * const enum_mclk[] = {
"SYSCLK",
@@ -125,9 +133,9 @@ static int mop500_ab8500_set_mclk(struct device *dev,
static int mclk_input_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mop500_ab8500_drvdata *drvdata =
- snd_soc_card_get_drvdata(codec->card);
+ snd_soc_card_get_drvdata(card);
ucontrol->value.enumerated.item[0] = drvdata->mclk_sel;
@@ -137,9 +145,9 @@ static int mclk_input_control_get(struct snd_kcontrol *kcontrol,
static int mclk_input_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mop500_ab8500_drvdata *drvdata =
- snd_soc_card_get_drvdata(codec->card);
+ snd_soc_card_get_drvdata(card);
unsigned int val = ucontrol->value.enumerated.item[0];
if (val > (unsigned int)MCLK_ULPCLK)
@@ -160,16 +168,6 @@ static struct snd_kcontrol_new mop500_ab8500_ctrls[] = {
SOC_ENUM_EXT("Master Clock Select",
soc_enum_mclk,
mclk_input_control_get, mclk_input_control_put),
- /* Digital interface - Clocks */
- SOC_SINGLE("Digital Interface Master Generator Switch",
- AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENMASTGEN,
- 1, 0),
- SOC_SINGLE("Digital Interface 0 Bit-clock Switch",
- AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENFSBITCLK0,
- 1, 0),
- SOC_SINGLE("Digital Interface 1 Bit-clock Switch",
- AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENFSBITCLK1,
- 1, 0),
SOC_DAPM_PIN_SWITCH("Headset Left"),
SOC_DAPM_PIN_SWITCH("Headset Right"),
SOC_DAPM_PIN_SWITCH("Earpiece"),
@@ -193,7 +191,7 @@ static struct snd_kcontrol_new mop500_ab8500_ctrls[] = {
/* ASoC */
-int mop500_ab8500_startup(struct snd_pcm_substream *substream)
+static int mop500_ab8500_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -202,7 +200,7 @@ int mop500_ab8500_startup(struct snd_pcm_substream *substream)
snd_soc_card_get_drvdata(rtd->card));
}
-void mop500_ab8500_shutdown(struct snd_pcm_substream *substream)
+static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->card->dev;
@@ -216,7 +214,7 @@ void mop500_ab8500_shutdown(struct snd_pcm_substream *substream)
rx_slots = DEF_RX_SLOTS;
}
-int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,
+static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -240,6 +238,21 @@ int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,
substream->name,
substream->number);
+ /* Ensure configuration consistency between DAIs */
+ mutex_lock(&mop500_ab8500_params_lock);
+ if (mop500_ab8500_usage) {
+ if (mop500_ab8500_rate != params_rate(params) ||
+ mop500_ab8500_channels != params_channels(params)) {
+ mutex_unlock(&mop500_ab8500_params_lock);
+ return -EBUSY;
+ }
+ } else {
+ mop500_ab8500_rate = params_rate(params);
+ mop500_ab8500_channels = params_channels(params);
+ }
+ __set_bit(cpu_dai->id, &mop500_ab8500_usage);
+ mutex_unlock(&mop500_ab8500_params_lock);
+
channels = params_channels(params);
switch (params_format(params)) {
@@ -338,9 +351,22 @@ int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ mutex_lock(&mop500_ab8500_params_lock);
+ __clear_bit(cpu_dai->id, &mop500_ab8500_usage);
+ mutex_unlock(&mop500_ab8500_params_lock);
+
+ return 0;
+}
+
struct snd_soc_ops mop500_ab8500_ops[] = {
{
.hw_params = mop500_ab8500_hw_params,
+ .hw_free = mop500_ab8500_hw_free,
.startup = mop500_ab8500_startup,
.shutdown = mop500_ab8500_shutdown,
}
@@ -385,7 +411,7 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd)
drvdata->mclk_sel = MCLK_ULPCLK;
/* Add controls */
- ret = snd_soc_add_codec_controls(codec, mop500_ab8500_ctrls,
+ ret = snd_soc_add_card_controls(codec->card, mop500_ab8500_ctrls,
ARRAY_SIZE(mop500_ab8500_ctrls));
if (ret < 0) {
pr_err("%s: Failed to add machine-controls (%d)!\n",
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 7d5fc1328523..c6fb5cce980e 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -658,14 +658,11 @@ static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
- drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx;
- drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx;
+ dai->playback_dma_data = &drvdata->msp->playback_dma_data;
+ dai->capture_dma_data = &drvdata->msp->capture_dma_data;
- dai->playback_dma_data = &drvdata->playback_dma_data;
- dai->capture_dma_data = &drvdata->capture_dma_data;
-
- drvdata->playback_dma_data.data_size = drvdata->slot_width;
- drvdata->capture_dma_data.data_size = drvdata->slot_width;
+ drvdata->msp->playback_dma_data.data_size = drvdata->slot_width;
+ drvdata->msp->capture_dma_data.data_size = drvdata->slot_width;
return 0;
}
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
index f53104359f15..312ae535e351 100644
--- a/sound/soc/ux500/ux500_msp_dai.h
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -51,15 +51,11 @@ enum ux500_msp_clock_id {
struct ux500_msp_i2s_drvdata {
struct ux500_msp *msp;
struct regulator *reg_vape;
- struct ux500_msp_dma_params playback_dma_data;
- struct ux500_msp_dma_params capture_dma_data;
unsigned int fmt;
unsigned int tx_mask;
unsigned int rx_mask;
int slots;
int slot_width;
- u8 configured;
- int data_delay;
/* Clocks */
unsigned int master_clk;
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index f2db6c90a9e2..1ca8b08ae993 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
@@ -26,9 +25,6 @@
#include "ux500_msp_i2s.h"
-/* MSP1/3 Tx/Rx usage protection */
-static DEFINE_SPINLOCK(msp_rxtx_lock);
-
/* Protocol desciptors */
static const struct msp_protdesc prot_descs[] = {
{ /* I2S */
@@ -356,24 +352,8 @@ static int configure_multichannel(struct ux500_msp *msp,
static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
{
- int status = 0, retval = 0;
+ int status = 0;
u32 reg_val_DMACR, reg_val_GCR;
- unsigned long flags;
-
- /* Check msp state whether in RUN or CONFIGURED Mode */
- if (msp->msp_state == MSP_STATE_IDLE) {
- spin_lock_irqsave(&msp_rxtx_lock, flags);
- if (msp->pinctrl_rxtx_ref == 0 &&
- !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_def))) {
- retval = pinctrl_select_state(msp->pinctrl_p,
- msp->pinctrl_def);
- if (retval)
- pr_err("could not set MSP defstate\n");
- }
- if (!retval)
- msp->pinctrl_rxtx_ref++;
- spin_unlock_irqrestore(&msp_rxtx_lock, flags);
- }
/* Configure msp with protocol dependent settings */
configure_protocol(msp, config);
@@ -387,12 +367,14 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
}
/* Make sure the correct DMA-directions are configured */
- if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) {
+ if ((config->direction & MSP_DIR_RX) &&
+ !msp->capture_dma_data.dma_cfg) {
dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
__func__);
return -EINVAL;
}
- if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) {
+ if ((config->direction == MSP_DIR_TX) &&
+ !msp->playback_dma_data.dma_cfg) {
dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
__func__);
return -EINVAL;
@@ -630,8 +612,7 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
{
- int status = 0, retval = 0;
- unsigned long flags;
+ int status = 0;
dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
@@ -643,18 +624,6 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
(~(FRAME_GEN_ENABLE | SRG_ENABLE))),
msp->registers + MSP_GCR);
- spin_lock_irqsave(&msp_rxtx_lock, flags);
- WARN_ON(!msp->pinctrl_rxtx_ref);
- msp->pinctrl_rxtx_ref--;
- if (msp->pinctrl_rxtx_ref == 0 &&
- !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_sleep))) {
- retval = pinctrl_select_state(msp->pinctrl_p,
- msp->pinctrl_sleep);
- if (retval)
- pr_err("could not set MSP sleepstate\n");
- }
- spin_unlock_irqrestore(&msp_rxtx_lock, flags);
-
writel(0, msp->registers + MSP_GCR);
writel(0, msp->registers + MSP_TCF);
writel(0, msp->registers + MSP_RCF);
@@ -682,7 +651,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
struct msp_i2s_platform_data *platform_data)
{
struct resource *res = NULL;
- struct i2s_controller *i2s_cont;
struct device_node *np = pdev->dev.of_node;
struct ux500_msp *msp;
@@ -707,8 +675,8 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
msp->id = platform_data->id;
msp->dev = &pdev->dev;
- msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
- msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+ msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx;
+ msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
@@ -717,6 +685,9 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
return -ENOMEM;
}
+ msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR;
+ msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR;
+
msp->registers = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (msp->registers == NULL) {
@@ -727,41 +698,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
msp->msp_state = MSP_STATE_IDLE;
msp->loopback_enable = 0;
- /* I2S-controller is allocated and added in I2S controller class. */
- i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL);
- if (!i2s_cont) {
- dev_err(&pdev->dev,
- "%s: ERROR: Failed to allocate I2S-controller!\n",
- __func__);
- return -ENOMEM;
- }
- i2s_cont->dev.parent = &pdev->dev;
- i2s_cont->data = (void *)msp;
- i2s_cont->id = (s16)msp->id;
- snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x",
- msp->id);
- dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);
- msp->i2s_cont = i2s_cont;
-
- msp->pinctrl_p = pinctrl_get(msp->dev);
- if (IS_ERR(msp->pinctrl_p))
- dev_err(&pdev->dev, "could not get MSP pinctrl\n");
- else {
- msp->pinctrl_def = pinctrl_lookup_state(msp->pinctrl_p,
- PINCTRL_STATE_DEFAULT);
- if (IS_ERR(msp->pinctrl_def)) {
- dev_err(&pdev->dev,
- "could not get MSP defstate (%li)\n",
- PTR_ERR(msp->pinctrl_def));
- }
- msp->pinctrl_sleep = pinctrl_lookup_state(msp->pinctrl_p,
- PINCTRL_STATE_SLEEP);
- if (IS_ERR(msp->pinctrl_sleep))
- dev_err(&pdev->dev,
- "could not get MSP idlestate (%li)\n",
- PTR_ERR(msp->pinctrl_def));
- }
-
return 0;
}
@@ -769,8 +705,6 @@ void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
struct ux500_msp *msp)
{
dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
-
- device_unregister(&msp->i2s_cont->dev);
}
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index e5cd105c90f9..258d0bcee0bd 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -16,6 +16,7 @@
#define UX500_MSP_I2S_H
#include <linux/platform_device.h>
+#include <linux/platform_data/asoc-ux500-msp.h>
#define MSP_INPUT_FREQ_APB 48000000
@@ -341,11 +342,6 @@ enum msp_compress_mode {
MSP_COMPRESS_MODE_A_LAW = 3
};
-enum msp_spi_burst_mode {
- MSP_SPI_BURST_MODE_DISABLE = 0,
- MSP_SPI_BURST_MODE_ENABLE = 1
-};
-
enum msp_expand_mode {
MSP_EXPAND_MODE_LINEAR = 0,
MSP_EXPAND_MODE_LINEAR_SIGNED = 1,
@@ -370,13 +366,6 @@ enum msp_protocol {
*/
#define MAX_MSP_BACKUP_REGS 36
-enum enum_i2s_controller {
- MSP_0_I2S_CONTROLLER = 0,
- MSP_1_I2S_CONTROLLER,
- MSP_2_I2S_CONTROLLER,
- MSP_3_I2S_CONTROLLER,
-};
-
enum i2s_direction_t {
MSP_DIR_TX = 0x01,
MSP_DIR_RX = 0x02,
@@ -454,32 +443,6 @@ struct msp_protdesc {
u32 clocks_per_frame;
};
-struct i2s_message {
- enum i2s_direction_t i2s_direction;
- void *txdata;
- void *rxdata;
- size_t txbytes;
- size_t rxbytes;
- int dma_flag;
- int tx_offset;
- int rx_offset;
- bool cyclic_dma;
- dma_addr_t buf_addr;
- size_t buf_len;
- size_t period_len;
-};
-
-struct i2s_controller {
- struct module *owner;
- unsigned int id;
- unsigned int class;
- const struct i2s_algorithm *algo; /* the algorithm to access the bus */
- void *data;
- struct mutex bus_lock;
- struct device dev; /* the controller device */
- char name[48];
-};
-
struct ux500_msp_config {
unsigned int f_inputclk;
unsigned int rx_clk_sel;
@@ -491,8 +454,6 @@ struct ux500_msp_config {
unsigned int tx_fsync_sel;
unsigned int rx_fifo_config;
unsigned int tx_fifo_config;
- unsigned int spi_clk_mode;
- unsigned int spi_burst_mode;
unsigned int loopback_enable;
unsigned int tx_data_enable;
unsigned int default_protdesc;
@@ -502,43 +463,28 @@ struct ux500_msp_config {
unsigned int direction;
unsigned int protocol;
unsigned int frame_freq;
- unsigned int frame_size;
enum msp_data_size data_size;
unsigned int def_elem_len;
unsigned int iodelay;
- void (*handler) (void *data);
- void *tx_callback_data;
- void *rx_callback_data;
+};
+
+struct ux500_msp_dma_params {
+ unsigned int data_size;
+ dma_addr_t tx_rx_addr;
+ struct stedma40_chan_cfg *dma_cfg;
};
struct ux500_msp {
- enum enum_i2s_controller id;
+ enum msp_i2s_id id;
void __iomem *registers;
struct device *dev;
- struct i2s_controller *i2s_cont;
- struct stedma40_chan_cfg *dma_cfg_rx;
- struct stedma40_chan_cfg *dma_cfg_tx;
- struct dma_chan *tx_pipeid;
- struct dma_chan *rx_pipeid;
+ struct ux500_msp_dma_params playback_dma_data;
+ struct ux500_msp_dma_params capture_dma_data;
enum msp_state msp_state;
- int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
- struct timer_list notify_timer;
int def_elem_len;
unsigned int dir_busy;
int loopback_enable;
- u32 backup_regs[MAX_MSP_BACKUP_REGS];
unsigned int f_bitclk;
- /* Pin modes */
- struct pinctrl *pinctrl_p;
- struct pinctrl_state *pinctrl_def;
- struct pinctrl_state *pinctrl_sleep;
- /* Reference Count */
- int pinctrl_rxtx_ref;
-};
-
-struct ux500_msp_dma_params {
- unsigned int data_size;
- struct stedma40_chan_cfg *dma_cfg;
};
struct msp_i2s_platform_data;
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index b6e5ae277299..ce554de5d9dc 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -76,20 +76,20 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
dma_params = snd_soc_dai_get_dma_data(dai, substream);
dma_cfg = dma_params->dma_cfg;
- mem_data_width = STEDMA40_HALFWORD_WIDTH;
+ mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
switch (dma_params->data_size) {
case 32:
- per_data_width = STEDMA40_WORD_WIDTH;
+ per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
case 16:
- per_data_width = STEDMA40_HALFWORD_WIDTH;
+ per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case 8:
- per_data_width = STEDMA40_BYTE_WIDTH;
+ per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
default:
- per_data_width = STEDMA40_WORD_WIDTH;
+ per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -103,10 +103,40 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg);
}
+static int ux500_pcm_prepare_slave_config(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 ux500_msp_dma_params *dma_params;
+ struct stedma40_chan_cfg *dma_cfg;
+ int ret;
+
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dma_cfg = dma_params->dma_cfg;
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
+ if (ret)
+ return ret;
+
+ slave_config->dst_maxburst = 4;
+ slave_config->dst_addr_width = dma_cfg->dst_info.data_width;
+ slave_config->src_maxburst = 4;
+ slave_config->src_addr_width = dma_cfg->src_info.data_width;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ slave_config->dst_addr = dma_params->tx_rx_addr;
+ else
+ slave_config->src_addr = dma_params->tx_rx_addr;
+
+ return 0;
+}
+
static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = {
.pcm_hardware = &ux500_pcm_hw,
.compat_request_channel = ux500_pcm_request_chan,
.prealloc_buffer_size = 128 * 1024,
+ .prepare_slave_config = ux500_pcm_prepare_slave_config,
};
int ux500_pcm_register_platform(struct platform_device *pdev)
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 359753fc24e1..45759f4cca75 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -292,7 +292,7 @@ retry:
}
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
- NULL, s->name+6);
+ NULL, "%s", s->name+6);
return s->unit_minor;
fail:
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 75e6016d3efe..eee7afcae375 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2670,8 +2670,6 @@ static int dbri_remove(struct platform_device *op)
snd_dbri_free(card->private_data);
snd_card_free(card);
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index a1a24b979ed2..8e3d9a6c7a3b 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -1070,7 +1070,6 @@ out:
ssc_free(chip->ssc);
snd_card_free(card);
- dev_set_drvdata(&spi->dev, NULL);
return 0;
}
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
index 4394ae796356..c39c77978468 100644
--- a/sound/usb/6fire/chip.c
+++ b/sound/usb/6fire/chip.c
@@ -30,7 +30,7 @@
MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
+MODULE_SUPPORTED_DEVICE("{{TerraTec,DMX 6Fire USB}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index 40dd50a80f55..c5b9cac37dc4 100644
--- a/sound/usb/6fire/pcm.c
+++ b/sound/usb/6fire/pcm.c
@@ -450,13 +450,13 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
struct snd_pcm_hw_params *hw_params)
{
- return snd_pcm_lib_malloc_pages(alsa_sub,
- params_buffer_bytes(hw_params));
+ return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub,
+ params_buffer_bytes(hw_params));
}
static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
{
- return snd_pcm_lib_free_pages(alsa_sub);
+ return snd_pcm_lib_free_vmalloc_buffer(alsa_sub);
}
static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
@@ -560,6 +560,8 @@ static struct snd_pcm_ops pcm_ops = {
.prepare = usb6fire_pcm_prepare,
.trigger = usb6fire_pcm_trigger,
.pointer = usb6fire_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
};
static void usb6fire_pcm_init_urb(struct pcm_urb *urb,
@@ -622,10 +624,6 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops);
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- MAX_BUFSIZE, MAX_BUFSIZE);
if (ret) {
kfree(rt);
snd_printk(KERN_ERR PREFIX
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 225dfd737265..de9408b83f75 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -115,5 +115,36 @@ config SND_USB_6FIRE
and further help can be found at
http://sixfireusb.sourceforge.net
+config SND_USB_HIFACE
+ tristate "M2Tech hiFace USB-SPDIF driver"
+ select SND_PCM
+ help
+ Select this option to include support for M2Tech hiFace USB-SPDIF
+ interface.
+
+ This driver supports the original M2Tech hiFace and some other
+ compatible devices. The supported products are:
+
+ * M2Tech Young
+ * M2Tech hiFace
+ * M2Tech North Star
+ * M2Tech W4S Young
+ * M2Tech Corrson
+ * M2Tech AUDIA
+ * M2Tech SL Audio
+ * M2Tech Empirical
+ * M2Tech Rockna
+ * M2Tech Pathos
+ * M2Tech Metronome
+ * M2Tech CAD
+ * M2Tech Audio Esclusive
+ * M2Tech Rotel
+ * M2Tech Eeaudio
+ * The Chord Company CHORD
+ * AVA Group A/S Vitus
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-hiface.
+
endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index ac256dc4c6be..abe668f660d1 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index c1916184e2e1..7103b0908d13 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -183,14 +183,15 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
struct snd_pcm_hw_params *hw_params)
{
- return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
+ return snd_pcm_lib_alloc_vmalloc_buffer(sub,
+ params_buffer_bytes(hw_params));
}
static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
{
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
deactivate_substream(cdev, sub);
- return snd_pcm_lib_free_pages(sub);
+ return snd_pcm_lib_free_vmalloc_buffer(sub);
}
/* this should probably go upstream */
@@ -345,7 +346,9 @@ static struct snd_pcm_ops snd_usb_caiaq_ops = {
.hw_free = snd_usb_caiaq_pcm_hw_free,
.prepare = snd_usb_caiaq_pcm_prepare,
.trigger = snd_usb_caiaq_pcm_trigger,
- .pointer = snd_usb_caiaq_pcm_pointer
+ .pointer = snd_usb_caiaq_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
};
static void check_for_elapsed_periods(struct snd_usb_caiaqdev *cdev,
@@ -852,11 +855,6 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_usb_caiaq_ops);
- snd_pcm_lib_preallocate_pages_for_all(cdev->pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
-
cdev->data_cb_info =
kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS,
GFP_KERNEL);
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index 48b63ccc78c7..1a61dd12fe38 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -39,25 +39,24 @@
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("caiaq USB audio");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
- "{Native Instruments, RigKontrol3},"
- "{Native Instruments, Kore Controller},"
- "{Native Instruments, Kore Controller 2},"
- "{Native Instruments, Audio Kontrol 1},"
- "{Native Instruments, Audio 2 DJ},"
- "{Native Instruments, Audio 4 DJ},"
- "{Native Instruments, Audio 8 DJ},"
- "{Native Instruments, Traktor Audio 2},"
- "{Native Instruments, Session I/O},"
- "{Native Instruments, GuitarRig mobile},"
- "{Native Instruments, Traktor Kontrol X1},"
- "{Native Instruments, Traktor Kontrol S4},"
- "{Native Instruments, Maschine Controller}}");
+MODULE_SUPPORTED_DEVICE("{{Native Instruments,RigKontrol2},"
+ "{Native Instruments,RigKontrol3},"
+ "{Native Instruments,Kore Controller},"
+ "{Native Instruments,Kore Controller 2},"
+ "{Native Instruments,Audio Kontrol 1},"
+ "{Native Instruments,Audio 2 DJ},"
+ "{Native Instruments,Audio 4 DJ},"
+ "{Native Instruments,Audio 8 DJ},"
+ "{Native Instruments,Traktor Audio 2},"
+ "{Native Instruments,Session I/O},"
+ "{Native Instruments,GuitarRig mobile},"
+ "{Native Instruments,Traktor Kontrol X1},"
+ "{Native Instruments,Traktor Kontrol S4},"
+ "{Native Instruments,Maschine Controller}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int snd_card_used[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the caiaq sound device");
@@ -388,7 +387,7 @@ static int create_card(struct usb_device *usb_dev,
struct snd_usb_caiaqdev *cdev;
for (devnum = 0; devnum < SNDRV_CARDS; devnum++)
- if (enable[devnum] && !snd_card_used[devnum])
+ if (enable[devnum])
break;
if (devnum >= SNDRV_CARDS)
diff --git a/sound/usb/card.h b/sound/usb/card.h
index bf2889a2cae5..5ecacaa90b53 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -21,6 +21,7 @@ struct audioformat {
unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */
+ unsigned char protocol; /* UAC_VERSION_1/2 */
unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 3a2ce390e278..86f80c60b21f 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -407,9 +407,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
- switch (altsd->bInterfaceProtocol) {
+ switch (fmt->protocol) {
case UAC_VERSION_1:
default:
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 99299ffb33ac..3525231c6b97 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -43,13 +43,12 @@
*/
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp,
- unsigned int format, void *_fmt,
- int protocol)
+ unsigned int format, void *_fmt)
{
int sample_width, sample_bytes;
u64 pcm_formats = 0;
- switch (protocol) {
+ switch (fp->protocol) {
case UAC_VERSION_1:
default: {
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
@@ -360,11 +359,8 @@ err:
*/
static int parse_audio_format_i(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format,
- struct uac_format_type_i_continuous_descriptor *fmt,
- struct usb_host_interface *iface)
+ struct uac_format_type_i_continuous_descriptor *fmt)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(iface);
- int protocol = altsd->bInterfaceProtocol;
snd_pcm_format_t pcm_format;
int ret;
@@ -387,8 +383,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
}
fp->formats = pcm_format_to_bits(pcm_format);
} else {
- fp->formats = parse_audio_format_i_type(chip, fp, format,
- fmt, protocol);
+ fp->formats = parse_audio_format_i_type(chip, fp, format, fmt);
if (!fp->formats)
return -EINVAL;
}
@@ -398,11 +393,8 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
* proprietary class specific descriptor.
* audio class v2 uses class specific EP0 range requests for that.
*/
- switch (protocol) {
+ switch (fp->protocol) {
default:
- snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n",
- chip->dev->devnum, fp->iface, fp->altsetting, protocol);
- /* fall through */
case UAC_VERSION_1:
fp->channels = fmt->bNrChannels;
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
@@ -427,12 +419,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/
static int parse_audio_format_ii(struct snd_usb_audio *chip,
struct audioformat *fp,
- int format, void *_fmt,
- struct usb_host_interface *iface)
+ int format, void *_fmt)
{
int brate, framesize, ret;
- struct usb_interface_descriptor *altsd = get_iface_desc(iface);
- int protocol = altsd->bInterfaceProtocol;
switch (format) {
case UAC_FORMAT_TYPE_II_AC3:
@@ -452,11 +441,8 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
fp->channels = 1;
- switch (protocol) {
+ switch (fp->protocol) {
default:
- snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n",
- chip->dev->devnum, fp->iface, fp->altsetting, protocol);
- /* fall through */
case UAC_VERSION_1: {
struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
brate = le16_to_cpu(fmt->wMaxBitRate);
@@ -483,17 +469,17 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format,
struct uac_format_type_i_continuous_descriptor *fmt,
- int stream, struct usb_host_interface *iface)
+ int stream)
{
int err;
switch (fmt->bFormatType) {
case UAC_FORMAT_TYPE_I:
case UAC_FORMAT_TYPE_III:
- err = parse_audio_format_i(chip, fp, format, fmt, iface);
+ err = parse_audio_format_i(chip, fp, format, fmt);
break;
case UAC_FORMAT_TYPE_II:
- err = parse_audio_format_ii(chip, fp, format, fmt, iface);
+ err = parse_audio_format_ii(chip, fp, format, fmt);
break;
default:
snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
diff --git a/sound/usb/format.h b/sound/usb/format.h
index 6f315226f320..4b8a01129f24 100644
--- a/sound/usb/format.h
+++ b/sound/usb/format.h
@@ -4,6 +4,6 @@
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format,
struct uac_format_type_i_continuous_descriptor *fmt,
- int stream, struct usb_host_interface *iface);
+ int stream);
#endif /* __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/hiface/Makefile b/sound/usb/hiface/Makefile
new file mode 100644
index 000000000000..463b136d1d89
--- /dev/null
+++ b/sound/usb/hiface/Makefile
@@ -0,0 +1,2 @@
+snd-usb-hiface-objs := chip.o pcm.o
+obj-$(CONFIG_SND_USB_HIFACE) += snd-usb-hiface.o
diff --git a/sound/usb/hiface/chip.c b/sound/usb/hiface/chip.c
new file mode 100644
index 000000000000..b0dcb3924ce5
--- /dev/null
+++ b/sound/usb/hiface/chip.c
@@ -0,0 +1,297 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * 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/slab.h>
+#include <sound/initval.h>
+
+#include "chip.h"
+#include "pcm.h"
+
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
+MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{M2Tech,Young},"
+ "{M2Tech,hiFace},"
+ "{M2Tech,North Star},"
+ "{M2Tech,W4S Young},"
+ "{M2Tech,Corrson},"
+ "{M2Tech,AUDIA},"
+ "{M2Tech,SL Audio},"
+ "{M2Tech,Empirical},"
+ "{M2Tech,Rockna},"
+ "{M2Tech,Pathos},"
+ "{M2Tech,Metronome},"
+ "{M2Tech,CAD},"
+ "{M2Tech,Audio Esclusive},"
+ "{M2Tech,Rotel},"
+ "{M2Tech,Eeaudio},"
+ "{The Chord Company,CHORD},"
+ "{AVA Group A/S,Vitus}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+#define DRIVER_NAME "snd-usb-hiface"
+#define CARD_NAME "hiFace"
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+static DEFINE_MUTEX(register_mutex);
+
+struct hiface_vendor_quirk {
+ const char *device_name;
+ u8 extra_freq;
+};
+
+static int hiface_chip_create(struct usb_device *device, int idx,
+ const struct hiface_vendor_quirk *quirk,
+ struct hiface_chip **rchip)
+{
+ struct snd_card *card = NULL;
+ struct hiface_chip *chip;
+ int ret;
+ int len;
+
+ *rchip = NULL;
+
+ /* if we are here, card can be registered in alsa. */
+ ret = snd_card_create(index[idx], id[idx], THIS_MODULE, sizeof(*chip), &card);
+ if (ret < 0) {
+ dev_err(&device->dev, "cannot create alsa card.\n");
+ return ret;
+ }
+
+ strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+
+ if (quirk && quirk->device_name)
+ strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname));
+ else
+ strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
+
+ strlcat(card->longname, card->shortname, sizeof(card->longname));
+ len = strlcat(card->longname, " at ", sizeof(card->longname));
+ if (len < sizeof(card->longname))
+ usb_make_path(device, card->longname + len,
+ sizeof(card->longname) - len);
+
+ chip = card->private_data;
+ chip->dev = device;
+ chip->card = card;
+
+ *rchip = chip;
+ return 0;
+}
+
+static int hiface_chip_probe(struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
+{
+ const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
+ int ret;
+ int i;
+ struct hiface_chip *chip;
+ struct usb_device *device = interface_to_usbdev(intf);
+
+ ret = usb_set_interface(device, 0, 0);
+ if (ret != 0) {
+ dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
+ return -EIO;
+ }
+
+ /* check whether the card is already registered */
+ chip = NULL;
+ mutex_lock(&register_mutex);
+
+ for (i = 0; i < SNDRV_CARDS; i++)
+ if (enable[i])
+ break;
+
+ if (i >= SNDRV_CARDS) {
+ dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = hiface_chip_create(device, i, quirk, &chip);
+ if (ret < 0)
+ goto err;
+
+ snd_card_set_dev(chip->card, &intf->dev);
+
+ ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
+ if (ret < 0)
+ goto err_chip_destroy;
+
+ ret = snd_card_register(chip->card);
+ if (ret < 0) {
+ dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
+ goto err_chip_destroy;
+ }
+
+ mutex_unlock(&register_mutex);
+
+ usb_set_intfdata(intf, chip);
+ return 0;
+
+err_chip_destroy:
+ snd_card_free(chip->card);
+err:
+ mutex_unlock(&register_mutex);
+ return ret;
+}
+
+static void hiface_chip_disconnect(struct usb_interface *intf)
+{
+ struct hiface_chip *chip;
+ struct snd_card *card;
+
+ chip = usb_get_intfdata(intf);
+ if (!chip)
+ return;
+
+ card = chip->card;
+
+ /* Make sure that the userspace cannot create new request */
+ snd_card_disconnect(card);
+
+ hiface_pcm_abort(chip);
+ snd_card_free_when_closed(card);
+}
+
+static const struct usb_device_id device_table[] = {
+ {
+ USB_DEVICE(0x04b4, 0x0384),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Young",
+ .extra_freq = 1,
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x930b),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "hiFace",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931b),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "North Star",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "W4S Young",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931d),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Corrson",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931e),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "AUDIA",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931f),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "SL Audio",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x9320),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Empirical",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x9321),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Rockna",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9001),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Pathos",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9002),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Metronome",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9006),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "CAD",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9008),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Audio Esclusive",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Rotel",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x932c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Eeaudio",
+ }
+ },
+ {
+ USB_DEVICE(0x245f, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "CHORD",
+ }
+ },
+ {
+ USB_DEVICE(0x25c6, 0x9002),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Vitus",
+ }
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static struct usb_driver hiface_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = hiface_chip_probe,
+ .disconnect = hiface_chip_disconnect,
+ .id_table = device_table,
+};
+
+module_usb_driver(hiface_usb_driver);
diff --git a/sound/usb/hiface/chip.h b/sound/usb/hiface/chip.h
new file mode 100644
index 000000000000..189a1371b7c4
--- /dev/null
+++ b/sound/usb/hiface/chip.h
@@ -0,0 +1,30 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * 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 HIFACE_CHIP_H
+#define HIFACE_CHIP_H
+
+#include <linux/usb.h>
+#include <sound/core.h>
+
+struct pcm_runtime;
+
+struct hiface_chip {
+ struct usb_device *dev;
+ struct snd_card *card;
+ struct pcm_runtime *pcm;
+};
+#endif /* HIFACE_CHIP_H */
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
new file mode 100644
index 000000000000..6430ed2a9f65
--- /dev/null
+++ b/sound/usb/hiface/pcm.c
@@ -0,0 +1,621 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * 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/slab.h>
+#include <sound/pcm.h>
+
+#include "pcm.h"
+#include "chip.h"
+
+#define OUT_EP 0x2
+#define PCM_N_URBS 8
+#define PCM_PACKET_SIZE 4096
+#define PCM_BUFFER_SIZE (2 * PCM_N_URBS * PCM_PACKET_SIZE)
+
+struct pcm_urb {
+ struct hiface_chip *chip;
+
+ struct urb instance;
+ struct usb_anchor submitted;
+ u8 *buffer;
+};
+
+struct pcm_substream {
+ spinlock_t lock;
+ struct snd_pcm_substream *instance;
+
+ bool active;
+ snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */
+ snd_pcm_uframes_t period_off; /* current position in current period */
+};
+
+enum { /* pcm streaming states */
+ STREAM_DISABLED, /* no pcm streaming */
+ STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
+ STREAM_RUNNING, /* pcm streaming running */
+ STREAM_STOPPING
+};
+
+struct pcm_runtime {
+ struct hiface_chip *chip;
+ struct snd_pcm *instance;
+
+ struct pcm_substream playback;
+ bool panic; /* if set driver won't do anymore pcm on device */
+
+ struct pcm_urb out_urbs[PCM_N_URBS];
+
+ struct mutex stream_mutex;
+ u8 stream_state; /* one of STREAM_XXX */
+ u8 extra_freq;
+ wait_queue_head_t stream_wait_queue;
+ bool stream_wait_cond;
+};
+
+static const unsigned int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000,
+ 352800, 384000 };
+static const struct snd_pcm_hw_constraint_list constraints_extra_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hardware pcm_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH,
+
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+
+ .rate_min = 44100,
+ .rate_max = 192000, /* changes in hiface_pcm_open to support extra rates */
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PCM_BUFFER_SIZE,
+ .period_bytes_min = PCM_PACKET_SIZE,
+ .period_bytes_max = PCM_BUFFER_SIZE,
+ .periods_min = 2,
+ .periods_max = 1024
+};
+
+/* message values used to change the sample rate */
+#define HIFACE_SET_RATE_REQUEST 0xb0
+
+#define HIFACE_RATE_44100 0x43
+#define HIFACE_RATE_48000 0x4b
+#define HIFACE_RATE_88200 0x42
+#define HIFACE_RATE_96000 0x4a
+#define HIFACE_RATE_176400 0x40
+#define HIFACE_RATE_192000 0x48
+#define HIFACE_RATE_352000 0x58
+#define HIFACE_RATE_384000 0x68
+
+static int hiface_pcm_set_rate(struct pcm_runtime *rt, unsigned int rate)
+{
+ struct usb_device *device = rt->chip->dev;
+ u16 rate_value;
+ int ret;
+
+ /* We are already sure that the rate is supported here thanks to
+ * ALSA constraints
+ */
+ switch (rate) {
+ case 44100:
+ rate_value = HIFACE_RATE_44100;
+ break;
+ case 48000:
+ rate_value = HIFACE_RATE_48000;
+ break;
+ case 88200:
+ rate_value = HIFACE_RATE_88200;
+ break;
+ case 96000:
+ rate_value = HIFACE_RATE_96000;
+ break;
+ case 176400:
+ rate_value = HIFACE_RATE_176400;
+ break;
+ case 192000:
+ rate_value = HIFACE_RATE_192000;
+ break;
+ case 352000:
+ rate_value = HIFACE_RATE_352000;
+ break;
+ case 384000:
+ rate_value = HIFACE_RATE_384000;
+ break;
+ default:
+ dev_err(&device->dev, "Unsupported rate %d\n", rate);
+ return -EINVAL;
+ }
+
+ /*
+ * USBIO: Vendor 0xb0(wValue=0x0043, wIndex=0x0000)
+ * 43 b0 43 00 00 00 00 00
+ * USBIO: Vendor 0xb0(wValue=0x004b, wIndex=0x0000)
+ * 43 b0 4b 00 00 00 00 00
+ * This control message doesn't have any ack from the
+ * other side
+ */
+ ret = usb_control_msg(device, usb_sndctrlpipe(device, 0),
+ HIFACE_SET_RATE_REQUEST,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ rate_value, 0, NULL, 0, 100);
+ if (ret < 0) {
+ dev_err(&device->dev, "Error setting samplerate %d.\n", rate);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct pcm_substream *hiface_pcm_get_substream(struct snd_pcm_substream
+ *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct device *device = &rt->chip->dev->dev;
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &rt->playback;
+
+ dev_err(device, "Error getting pcm substream slot.\n");
+ return NULL;
+}
+
+/* call with stream_mutex locked */
+static void hiface_pcm_stream_stop(struct pcm_runtime *rt)
+{
+ int i, time;
+
+ if (rt->stream_state != STREAM_DISABLED) {
+ rt->stream_state = STREAM_STOPPING;
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ time = usb_wait_anchor_empty_timeout(
+ &rt->out_urbs[i].submitted, 100);
+ if (!time)
+ usb_kill_anchored_urbs(
+ &rt->out_urbs[i].submitted);
+ usb_kill_urb(&rt->out_urbs[i].instance);
+ }
+
+ rt->stream_state = STREAM_DISABLED;
+ }
+}
+
+/* call with stream_mutex locked */
+static int hiface_pcm_stream_start(struct pcm_runtime *rt)
+{
+ int ret = 0;
+ int i;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+
+ /* reset panic state when starting a new stream */
+ rt->panic = false;
+
+ /* submit our out urbs zero init */
+ rt->stream_state = STREAM_STARTING;
+ for (i = 0; i < PCM_N_URBS; i++) {
+ memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE);
+ usb_anchor_urb(&rt->out_urbs[i].instance,
+ &rt->out_urbs[i].submitted);
+ ret = usb_submit_urb(&rt->out_urbs[i].instance,
+ GFP_ATOMIC);
+ if (ret) {
+ hiface_pcm_stream_stop(rt);
+ return ret;
+ }
+ }
+
+ /* wait for first out urb to return (sent in in urb handler) */
+ wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
+ HZ);
+ if (rt->stream_wait_cond) {
+ struct device *device = &rt->chip->dev->dev;
+ dev_dbg(device, "%s: Stream is running wakeup event\n",
+ __func__);
+ rt->stream_state = STREAM_RUNNING;
+ } else {
+ hiface_pcm_stream_stop(rt);
+ return -EIO;
+ }
+ }
+ return ret;
+}
+
+/* The hardware wants word-swapped 32-bit values */
+static void memcpy_swahw32(u8 *dest, u8 *src, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n / 4; i++)
+ ((u32 *)dest)[i] = swahw32(((u32 *)src)[i]);
+}
+
+/* call with substream locked */
+/* returns true if a period elapsed */
+static bool hiface_pcm_playback(struct pcm_substream *sub, struct pcm_urb *urb)
+{
+ struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
+ struct device *device = &urb->chip->dev->dev;
+ u8 *source;
+ unsigned int pcm_buffer_size;
+
+ WARN_ON(alsa_rt->format != SNDRV_PCM_FORMAT_S32_LE);
+
+ pcm_buffer_size = snd_pcm_lib_buffer_bytes(sub->instance);
+
+ if (sub->dma_off + PCM_PACKET_SIZE <= pcm_buffer_size) {
+ dev_dbg(device, "%s: (1) buffer_size %#x dma_offset %#x\n", __func__,
+ (unsigned int) pcm_buffer_size,
+ (unsigned int) sub->dma_off);
+
+ source = alsa_rt->dma_area + sub->dma_off;
+ memcpy_swahw32(urb->buffer, source, PCM_PACKET_SIZE);
+ } else {
+ /* wrap around at end of ring buffer */
+ unsigned int len;
+
+ dev_dbg(device, "%s: (2) buffer_size %#x dma_offset %#x\n", __func__,
+ (unsigned int) pcm_buffer_size,
+ (unsigned int) sub->dma_off);
+
+ len = pcm_buffer_size - sub->dma_off;
+
+ source = alsa_rt->dma_area + sub->dma_off;
+ memcpy_swahw32(urb->buffer, source, len);
+
+ source = alsa_rt->dma_area;
+ memcpy_swahw32(urb->buffer + len, source,
+ PCM_PACKET_SIZE - len);
+ }
+ sub->dma_off += PCM_PACKET_SIZE;
+ if (sub->dma_off >= pcm_buffer_size)
+ sub->dma_off -= pcm_buffer_size;
+
+ sub->period_off += PCM_PACKET_SIZE;
+ if (sub->period_off >= alsa_rt->period_size) {
+ sub->period_off %= alsa_rt->period_size;
+ return true;
+ }
+ return false;
+}
+
+static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
+{
+ struct pcm_urb *out_urb = usb_urb->context;
+ struct pcm_runtime *rt = out_urb->chip->pcm;
+ struct pcm_substream *sub;
+ bool do_period_elapsed = false;
+ unsigned long flags;
+ int ret;
+
+ if (rt->panic || rt->stream_state == STREAM_STOPPING)
+ return;
+
+ if (unlikely(usb_urb->status == -ENOENT || /* unlinked */
+ usb_urb->status == -ENODEV || /* device removed */
+ usb_urb->status == -ECONNRESET || /* unlinked */
+ usb_urb->status == -ESHUTDOWN)) { /* device disabled */
+ goto out_fail;
+ }
+
+ if (rt->stream_state == STREAM_STARTING) {
+ rt->stream_wait_cond = true;
+ wake_up(&rt->stream_wait_queue);
+ }
+
+ /* now send our playback data (if a free out urb was found) */
+ sub = &rt->playback;
+ spin_lock_irqsave(&sub->lock, flags);
+ if (sub->active)
+ do_period_elapsed = hiface_pcm_playback(sub, out_urb);
+ else
+ memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
+
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ if (do_period_elapsed)
+ snd_pcm_period_elapsed(sub->instance);
+
+ ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
+ if (ret < 0)
+ goto out_fail;
+
+ return;
+
+out_fail:
+ rt->panic = true;
+}
+
+static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = NULL;
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+ int ret;
+
+ if (rt->panic)
+ return -EPIPE;
+
+ mutex_lock(&rt->stream_mutex);
+ alsa_rt->hw = pcm_hw;
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sub = &rt->playback;
+
+ if (!sub) {
+ struct device *device = &rt->chip->dev->dev;
+ mutex_unlock(&rt->stream_mutex);
+ dev_err(device, "Invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (rt->extra_freq) {
+ alsa_rt->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ alsa_rt->hw.rate_max = 384000;
+
+ /* explicit constraints needed as we added SNDRV_PCM_RATE_KNOT */
+ ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_extra_rates);
+ if (ret < 0) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ }
+
+ sub->instance = alsa_sub;
+ sub->active = false;
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ unsigned long flags;
+
+ if (rt->panic)
+ return 0;
+
+ mutex_lock(&rt->stream_mutex);
+ if (sub) {
+ hiface_pcm_stream_stop(rt);
+
+ /* deactivate substream */
+ spin_lock_irqsave(&sub->lock, flags);
+ sub->instance = NULL;
+ sub->active = false;
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub,
+ params_buffer_bytes(hw_params));
+}
+
+static int hiface_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(alsa_sub);
+}
+
+static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+ int ret;
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ mutex_lock(&rt->stream_mutex);
+
+ sub->dma_off = 0;
+ sub->period_off = 0;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+
+ ret = hiface_pcm_set_rate(rt, alsa_rt->rate);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ ret = hiface_pcm_stream_start(rt);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
+{
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irq(&sub->lock);
+ sub->active = true;
+ spin_unlock_irq(&sub->lock);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irq(&sub->lock);
+ sub->active = false;
+ spin_unlock_irq(&sub->lock);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ unsigned long flags;
+ snd_pcm_uframes_t dma_offset;
+
+ if (rt->panic || !sub)
+ return SNDRV_PCM_STATE_XRUN;
+
+ spin_lock_irqsave(&sub->lock, flags);
+ dma_offset = sub->dma_off;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ return bytes_to_frames(alsa_sub->runtime, dma_offset);
+}
+
+static struct snd_pcm_ops pcm_ops = {
+ .open = hiface_pcm_open,
+ .close = hiface_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = hiface_pcm_hw_params,
+ .hw_free = hiface_pcm_hw_free,
+ .prepare = hiface_pcm_prepare,
+ .trigger = hiface_pcm_trigger,
+ .pointer = hiface_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+static int hiface_pcm_init_urb(struct pcm_urb *urb,
+ struct hiface_chip *chip,
+ unsigned int ep,
+ void (*handler)(struct urb *))
+{
+ urb->chip = chip;
+ usb_init_urb(&urb->instance);
+
+ urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL);
+ if (!urb->buffer)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(&urb->instance, chip->dev,
+ usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer,
+ PCM_PACKET_SIZE, handler, urb);
+ init_usb_anchor(&urb->submitted);
+
+ return 0;
+}
+
+void hiface_pcm_abort(struct hiface_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+
+ if (rt) {
+ rt->panic = true;
+
+ mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+ mutex_unlock(&rt->stream_mutex);
+ }
+}
+
+static void hiface_pcm_destroy(struct hiface_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+ int i;
+
+ for (i = 0; i < PCM_N_URBS; i++)
+ kfree(rt->out_urbs[i].buffer);
+
+ kfree(chip->pcm);
+ chip->pcm = NULL;
+}
+
+static void hiface_pcm_free(struct snd_pcm *pcm)
+{
+ struct pcm_runtime *rt = pcm->private_data;
+
+ if (rt)
+ hiface_pcm_destroy(rt->chip);
+}
+
+int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
+{
+ int i;
+ int ret;
+ struct snd_pcm *pcm;
+ struct pcm_runtime *rt;
+
+ rt = kzalloc(sizeof(*rt), GFP_KERNEL);
+ if (!rt)
+ return -ENOMEM;
+
+ rt->chip = chip;
+ rt->stream_state = STREAM_DISABLED;
+ if (extra_freq)
+ rt->extra_freq = 1;
+
+ init_waitqueue_head(&rt->stream_wait_queue);
+ mutex_init(&rt->stream_mutex);
+ spin_lock_init(&rt->playback.lock);
+
+ for (i = 0; i < PCM_N_URBS; i++)
+ hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP,
+ hiface_pcm_out_urb_handler);
+
+ ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm);
+ if (ret < 0) {
+ kfree(rt);
+ dev_err(&chip->dev->dev, "Cannot create pcm instance\n");
+ return ret;
+ }
+
+ pcm->private_data = rt;
+ pcm->private_free = hiface_pcm_free;
+
+ strlcpy(pcm->name, "USB-SPDIF Audio", sizeof(pcm->name));
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
+
+ rt->instance = pcm;
+
+ chip->pcm = rt;
+ return 0;
+}
diff --git a/sound/usb/hiface/pcm.h b/sound/usb/hiface/pcm.h
new file mode 100644
index 000000000000..77edd7c12e19
--- /dev/null
+++ b/sound/usb/hiface/pcm.h
@@ -0,0 +1,24 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * 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 HIFACE_PCM_H
+#define HIFACE_PCM_H
+
+struct hiface_chip;
+
+int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq);
+void hiface_pcm_abort(struct hiface_chip *chip);
+#endif /* HIFACE_PCM_H */
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 8e01fa4991c5..b901f468b67a 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1575,8 +1575,41 @@ static struct port_info {
EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"),
EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"),
EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"),
+ /* BOSS GT-PRO */
+ CONTROL_PORT(0x0582, 0x0089, 0, "%s Control"),
/* Edirol UM-3EX */
CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"),
+ /* Roland VG-99 */
+ CONTROL_PORT(0x0582, 0x00b2, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x00b2, 1, "%s MIDI"),
+ /* Cakewalk Sonar V-Studio 100 */
+ EXTERNAL_PORT(0x0582, 0x00eb, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x00eb, 1, "%s Control"),
+ /* Roland VB-99 */
+ CONTROL_PORT(0x0582, 0x0102, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0102, 1, "%s MIDI"),
+ /* Roland A-PRO */
+ EXTERNAL_PORT(0x0582, 0x010f, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x010f, 1, "%s 1"),
+ CONTROL_PORT(0x0582, 0x010f, 2, "%s 2"),
+ /* Roland SD-50 */
+ ROLAND_SYNTH_PORT(0x0582, 0x0114, 0, "%s Synth", 128),
+ EXTERNAL_PORT(0x0582, 0x0114, 1, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0114, 2, "%s Control"),
+ /* Roland OCTA-CAPTURE */
+ EXTERNAL_PORT(0x0582, 0x0120, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0120, 1, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0121, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0121, 1, "%s Control"),
+ /* Roland SPD-SX */
+ CONTROL_PORT(0x0582, 0x0145, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0145, 1, "%s MIDI"),
+ /* Roland A-Series */
+ CONTROL_PORT(0x0582, 0x0156, 0, "%s Keyboard"),
+ EXTERNAL_PORT(0x0582, 0x0156, 1, "%s MIDI"),
+ /* Roland INTEGRA-7 */
+ ROLAND_SYNTH_PORT(0x0582, 0x015b, 0, "%s Synth", 128),
+ CONTROL_PORT(0x0582, 0x015b, 1, "%s Control"),
/* M-Audio MidiSport 8x8 */
CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"),
CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"),
@@ -1948,6 +1981,44 @@ static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi,
}
/*
+ * Detects the endpoints and ports of Roland devices.
+ */
+static int snd_usbmidi_detect_roland(struct snd_usb_midi* umidi,
+ struct snd_usb_midi_endpoint_info* endpoint)
+{
+ struct usb_interface* intf;
+ struct usb_host_interface *hostif;
+ u8* cs_desc;
+
+ intf = umidi->iface;
+ if (!intf)
+ return -ENOENT;
+ hostif = intf->altsetting;
+ /*
+ * Some devices have a descriptor <06 24 F1 02 <inputs> <outputs>>,
+ * some have standard class descriptors, or both kinds, or neither.
+ */
+ for (cs_desc = hostif->extra;
+ cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2;
+ cs_desc += cs_desc[0]) {
+ if (cs_desc[0] >= 6 &&
+ cs_desc[1] == USB_DT_CS_INTERFACE &&
+ cs_desc[2] == 0xf1 &&
+ cs_desc[3] == 0x02) {
+ endpoint->in_cables = (1 << cs_desc[4]) - 1;
+ endpoint->out_cables = (1 << cs_desc[5]) - 1;
+ return snd_usbmidi_detect_endpoints(umidi, endpoint, 1);
+ } else if (cs_desc[0] >= 7 &&
+ cs_desc[1] == USB_DT_CS_INTERFACE &&
+ cs_desc[2] == UAC_HEADER) {
+ return snd_usbmidi_get_ms_info(umidi, endpoint);
+ }
+ }
+
+ return -ENODEV;
+}
+
+/*
* Creates the endpoints and their ports for Midiman devices.
*/
static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi,
@@ -2162,6 +2233,9 @@ int snd_usbmidi_create(struct snd_card *card,
case QUIRK_MIDI_YAMAHA:
err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]);
break;
+ case QUIRK_MIDI_ROLAND:
+ err = snd_usbmidi_detect_roland(umidi, &endpoints[0]);
+ break;
case QUIRK_MIDI_MIDIMAN:
umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops;
memcpy(&endpoints[0], quirk->data,
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index 6ad617b94732..8b5d2c564e04 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -1349,7 +1349,7 @@ static void ua101_disconnect(struct usb_interface *interface)
snd_card_disconnect(ua->card);
/* make sure that there are no pending USB requests */
- __list_for_each(midi, &ua->midi_list)
+ list_for_each(midi, &ua->midi_list)
snd_usbmidi_disconnect(midi);
abort_alsa_playback(ua);
abort_alsa_capture(ua);
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index ebe91440a068..d42a584cf829 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -9,6 +9,8 @@
* Alan Cox (alan@lxorguk.ukuu.org.uk)
* Thomas Sailer (sailer@ife.ee.ethz.ch)
*
+ * Audio Advantage Micro II support added by:
+ * Przemek Rudy (prudy1@o2.pl)
*
* 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
@@ -30,6 +32,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/hwdep.h>
@@ -1315,6 +1318,211 @@ static struct std_mono_table ebox44_table[] = {
{}
};
+/* Audio Advantage Micro II findings:
+ *
+ * Mapping spdif AES bits to vendor register.bit:
+ * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
+ * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
+ * AES2: [0 0 0 0 0 0 0 0]
+ * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
+ * (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
+ *
+ * power on values:
+ * r2: 0x10
+ * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
+ * just after it to 0xa0, presumably it disables/mutes some analog
+ * parts when there is no audio.)
+ * r9: 0x28
+ *
+ * Optical transmitter on/off:
+ * vendor register.bit: 9.1
+ * 0 - on (0x28 register value)
+ * 1 - off (0x2a register value)
+ *
+ */
+static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ unsigned int ep;
+ unsigned char data[3];
+ int rate;
+
+ ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
+ ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = 0x00;
+
+ /* use known values for that card: interface#1 altsetting#1 */
+ iface = usb_ifnum_to_if(mixer->chip->dev, 1);
+ alts = &iface->altsetting[1];
+ ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_rcvctrlpipe(mixer->chip->dev, 0),
+ UAC_GET_CUR,
+ USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
+ UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
+ ep,
+ data,
+ sizeof(data));
+ if (err < 0)
+ goto end;
+
+ rate = data[0] | (data[1] << 8) | (data[2] << 16);
+ ucontrol->value.iec958.status[3] = (rate == 48000) ?
+ IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
+
+ err = 0;
+end:
+ return err;
+}
+
+static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ u8 reg;
+ unsigned long priv_backup = kcontrol->private_value;
+
+ reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) |
+ (ucontrol->value.iec958.status[0] & 0x0f);
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 2,
+ NULL,
+ 0);
+ if (err < 0)
+ goto end;
+
+ kcontrol->private_value &= 0xfffff0f0;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f);
+
+ reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ?
+ 0xa0 : 0x20;
+ reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f;
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 3,
+ NULL,
+ 0);
+ if (err < 0)
+ goto end;
+
+ kcontrol->private_value &= 0xffff0fff;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
+
+ /* The frequency bits in AES3 cannot be set via register access. */
+
+ /* Silently ignore any bits from the request that cannot be set. */
+
+ err = (priv_backup != kcontrol->private_value);
+end:
+ return err;
+}
+
+static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0x0f;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0x00;
+ ucontrol->value.iec958.status[3] = 0x00;
+
+ return 0;
+}
+
+static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
+
+ return 0;
+}
+
+static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 9,
+ NULL,
+ 0);
+
+ if (!err) {
+ err = (reg != (kcontrol->private_value & 0x0ff));
+ if (err)
+ kcontrol->private_value = reg;
+ }
+
+ return err;
+}
+
+static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = snd_microii_spdif_info,
+ .get = snd_microii_spdif_default_get,
+ .put = snd_microii_spdif_default_put,
+ .private_value = 0x00000100UL,/* reset value */
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .info = snd_microii_spdif_info,
+ .get = snd_microii_spdif_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_microii_spdif_switch_get,
+ .put = snd_microii_spdif_switch_put,
+ .private_value = 0x00000028UL,/* reset value */
+ }
+};
+
+static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
+ err = snd_ctl_add(mixer->chip->card,
+ snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer));
+ if (err < 0)
+ return err;
+ }
+
+ return err;
+}
+
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
{
int err = 0;
@@ -1353,6 +1561,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_xonar_u1_controls_create(mixer);
break;
+ case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
+ err = snd_microii_controls_create(mixer);
+ break;
+
case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
err = snd_nativeinstruments_create_mixer(mixer,
snd_nativeinstruments_ta6_mixers,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 93b6e32cfead..15b151ed4899 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -202,13 +202,11 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
/* if endpoint doesn't have pitch control, bail out */
if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
return 0;
- switch (altsd->bInterfaceProtocol) {
+ switch (fmt->protocol) {
case UAC_VERSION_1:
default:
return init_pitch_v1(chip, iface, alts, fmt);
@@ -300,6 +298,35 @@ static int deactivate_endpoints(struct snd_usb_substream *subs)
return 0;
}
+static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
+ unsigned int altsetting,
+ struct usb_host_interface **alts,
+ unsigned int *ep)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+
+ iface = usb_ifnum_to_if(dev, ifnum);
+ if (!iface || iface->num_altsetting < altsetting + 1)
+ return -ENOENT;
+ *alts = &iface->altsetting[altsetting];
+ altsd = get_iface_desc(*alts);
+ if (altsd->bAlternateSetting != altsetting ||
+ altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
+ (altsd->bInterfaceSubClass != 2 &&
+ altsd->bInterfaceProtocol != 2 ) ||
+ altsd->bNumEndpoints < 1)
+ return -ENOENT;
+ epd = get_endpoint(*alts, 0);
+ if (!usb_endpoint_is_isoc_in(epd) ||
+ (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
+ USB_ENDPOINT_USAGE_IMPLICIT_FB)
+ return -ENOENT;
+ *ep = epd->bEndpointAddress;
+ return 0;
+}
+
/*
* find a matching format and set up the interface
*/
@@ -395,6 +422,18 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
goto add_sync_ep;
}
}
+ if (is_playback &&
+ attr == USB_ENDPOINT_SYNC_ASYNC &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ altsd->bInterfaceProtocol == 2 &&
+ altsd->bNumEndpoints == 1 &&
+ USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
+ search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
+ altsd->bAlternateSetting,
+ &alts, &ep) >= 0) {
+ implicit_fb = 1;
+ goto add_sync_ep;
+ }
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
(!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 8b75bcf136f6..f5f0595ef9c7 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -461,6 +461,17 @@ YAMAHA_DEVICE(0x7000, "DTX"),
YAMAHA_DEVICE(0x7010, "UB99"),
#undef YAMAHA_DEVICE
#undef YAMAHA_INTERFACE
+/* this catches most recent vendor-specific Yamaha devices */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x0499,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUTODETECT
+ }
+},
/*
* Roland/RolandED/Edirol/BOSS devices
@@ -1136,7 +1147,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Roland M-1000 support */
{
/*
* Has ID 0x0038 when not in "Advanced Driver" mode;
@@ -1251,7 +1261,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol M-100FX support */
{
/* has ID 0x004e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x004c),
@@ -1371,20 +1380,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* has ID 0x006b when not in "Advanced Driver" mode */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x006a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SP-606",
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- }
-},
-{
/* has ID 0x006e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x006d),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -1471,8 +1466,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Roland V-SYNTH XT support */
- /* TODO: add BOSS GT-PRO support */
{
/* has ID 0x008c when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x008b),
@@ -1487,42 +1480,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol PC-80 support */
-{
- USB_DEVICE(0x0582, 0x0096),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "UA-1EX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x009a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "UM-3EX",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x000f,
- .in_cables = 0x000f
- }
- }
-},
{
/*
* This quirk is for the "Advanced Driver" mode. If off, the UA-4FX
@@ -1553,124 +1510,8 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol MD-P1 support */
-{
- USB_DEVICE(0x582, 0x00a6),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "Juno-G",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- }
-},
-{
- /* Roland SH-201 */
- USB_DEVICE(0x0582, 0x00ad),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SH-201",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- /* Advanced mode of the Roland VG-99, with MIDI and 24-bit PCM at 44.1
- * kHz. In standard mode, the device has ID 0582:00b3, and offers
- * 16-bit PCM at 44.1 kHz with no MIDI.
- */
- USB_DEVICE(0x0582, 0x00b2),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "VG-99",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0003,
- .in_cables = 0x0003
- }
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- /* Roland SonicCell */
- USB_DEVICE(0x0582, 0x00c2),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SonicCell",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- },
- {
- .ifnum = -1
- }
- }
- }
-},
{
/* Edirol M-16DX */
- /* FIXME: This quirk gives a good-working capture stream but the
- * playback seems problematic because of lacking of sync
- * with capture stream. It needs to sync with the capture
- * clock. As now, you'll get frequent sound distortions
- * via the playback.
- */
USB_DEVICE(0x0582, 0x00c4),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.ifnum = QUIRK_ANY_INTERFACE,
@@ -1699,35 +1540,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* BOSS GT-10 */
- USB_DEVICE(0x0582, 0x00da),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
/* Advanced modes of the Edirol UA-25EX.
* For the standard mode, UA-25EX has ID 0582:00e7, which
* offers only 16-bit PCM at 44.1 kHz and no MIDI.
@@ -1758,42 +1570,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* has ID 0x00ea when not in Advanced Driver mode */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "Roland", */
- /* .product_name = "UA-1G", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x0104),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "Roland", */
- /* .product_name = "UM-1G", */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- }
-},
-{
/* Edirol UM-3G */
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -1806,92 +1582,49 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* Boss JS-8 Jam Station */
- USB_DEVICE(0x0582, 0x0109),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "BOSS", */
- /* .product_name = "JS-8", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- /* has ID 0x0110 when not in Advanced Driver mode */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f),
+ /* only 44.1 kHz works at the moment */
+ USB_DEVICE(0x0582, 0x0120),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Roland", */
- /* .product_name = "A-PRO", */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0003,
- .in_cables = 0x0007
- }
- }
-},
-{
- /* Roland GAIA SH-01 */
- USB_DEVICE(0x0582, 0x0111),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "GAIA",
+ /* .product_name = "OCTO-CAPTURE", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &(const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0003,
- .in_cables = 0x0003
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x05,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
}
},
{
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x0113),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "BOSS", */
- /* .product_name = "ME-25", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 12,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x85,
+ .ep_attr = 0x25,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 2,
@@ -1902,30 +1635,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x0127),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "Roland", */
- /* .product_name = "GR-55", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
},
{
- .ifnum = 2,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ .ifnum = 4,
+ .type = QUIRK_IGNORE_INTERFACE
},
{
.ifnum = -1
@@ -1934,34 +1649,49 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* Added support for Roland UM-ONE which differs from UM-1 */
- USB_DEVICE(0x0582, 0x012a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "ROLAND", */
- /* .product_name = "UM-ONE", */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0003
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x011e),
+ /* only 44.1 kHz works at the moment */
+ USB_DEVICE(0x0582, 0x012f),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "BOSS", */
- /* .product_name = "BR-800", */
+ /* .vendor_name = "Roland", */
+ /* .product_name = "QUAD-CAPTURE", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x05,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 6,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x85,
+ .ep_attr = 0x25,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 2,
@@ -1972,38 +1702,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x0130),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "BOSS", */
- /* .product_name = "MICRO BR-80", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
+ .ifnum = 3,
.type = QUIRK_IGNORE_INTERFACE
},
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
+ .ifnum = 4,
+ .type = QUIRK_IGNORE_INTERFACE
},
{
.ifnum = -1
@@ -2011,34 +1715,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+/* this catches most recent vendor-specific Roland devices */
{
- USB_DEVICE(0x0582, 0x014d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "BOSS", */
- /* .product_name = "GT-100", */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x0582,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
.ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- },
- {
- .ifnum = -1
- }
- }
+ .type = QUIRK_AUTODETECT
}
},
@@ -3434,4 +3119,16 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+{
+ /*
+ * The original product_name is "USB Sound Device", however this name
+ * is also used by the CM106 based cards, so make it unique.
+ */
+ USB_DEVICE(0x0d8c, 0x0103),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .product_name = "Audio Advantage MicroII",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 3879eae7e874..5b01330b8452 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
#include <sound/control.h>
#include <sound/core.h>
@@ -175,6 +176,212 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
return 0;
}
+static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+ struct uac1_as_header_descriptor *ashd;
+ struct uac_format_type_i_discrete_descriptor *fmtd;
+
+ /*
+ * Most Roland/Yamaha audio streaming interfaces have more or less
+ * standard descriptors, but older devices might lack descriptors, and
+ * future ones might change, so ensure that we fail silently if the
+ * interface doesn't look exactly right.
+ */
+
+ /* must have a non-zero altsetting for streaming */
+ if (iface->num_altsetting < 2)
+ return -ENODEV;
+ alts = &iface->altsetting[1];
+ altsd = get_iface_desc(alts);
+
+ /* must have an isochronous endpoint for streaming */
+ if (altsd->bNumEndpoints < 1)
+ return -ENODEV;
+ epd = get_endpoint(alts, 0);
+ if (!usb_endpoint_xfer_isoc(epd))
+ return -ENODEV;
+
+ /* must have format descriptors */
+ ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_AS_GENERAL);
+ fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_FORMAT_TYPE);
+ if (!ashd || ashd->bLength < 7 ||
+ !fmtd || fmtd->bLength < 8)
+ return -ENODEV;
+
+ return create_standard_audio_quirk(chip, iface, driver, NULL);
+}
+
+static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ static const struct snd_usb_audio_quirk yamaha_midi_quirk = {
+ .type = QUIRK_MIDI_YAMAHA
+ };
+ struct usb_midi_in_jack_descriptor *injd;
+ struct usb_midi_out_jack_descriptor *outjd;
+
+ /* must have some valid jack descriptors */
+ injd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, USB_MS_MIDI_IN_JACK);
+ outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, USB_MS_MIDI_OUT_JACK);
+ if (!injd && !outjd)
+ return -ENODEV;
+ if (injd && (injd->bLength < 5 ||
+ (injd->bJackType != USB_MS_EMBEDDED &&
+ injd->bJackType != USB_MS_EXTERNAL)))
+ return -ENODEV;
+ if (outjd && (outjd->bLength < 6 ||
+ (outjd->bJackType != USB_MS_EMBEDDED &&
+ outjd->bJackType != USB_MS_EXTERNAL)))
+ return -ENODEV;
+ return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk);
+}
+
+static int create_roland_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ static const struct snd_usb_audio_quirk roland_midi_quirk = {
+ .type = QUIRK_MIDI_ROLAND
+ };
+ u8 *roland_desc = NULL;
+
+ /* might have a vendor-specific descriptor <06 24 F1 02 ...> */
+ for (;;) {
+ roland_desc = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen,
+ roland_desc, 0xf1);
+ if (!roland_desc)
+ return -ENODEV;
+ if (roland_desc[0] < 6 || roland_desc[3] != 2)
+ continue;
+ return create_any_midi_quirk(chip, iface, driver,
+ &roland_midi_quirk);
+ }
+}
+
+static int create_std_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ struct usb_ms_header_descriptor *mshd;
+ struct usb_ms_endpoint_descriptor *msepd;
+
+ /* must have the MIDIStreaming interface header descriptor*/
+ mshd = (struct usb_ms_header_descriptor *)alts->extra;
+ if (alts->extralen < 7 ||
+ mshd->bLength < 7 ||
+ mshd->bDescriptorType != USB_DT_CS_INTERFACE ||
+ mshd->bDescriptorSubtype != USB_MS_HEADER)
+ return -ENODEV;
+ /* must have the MIDIStreaming endpoint descriptor*/
+ msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra;
+ if (alts->endpoint[0].extralen < 4 ||
+ msepd->bLength < 4 ||
+ msepd->bDescriptorType != USB_DT_CS_ENDPOINT ||
+ msepd->bDescriptorSubtype != UAC_MS_GENERAL ||
+ msepd->bNumEmbMIDIJack < 1 ||
+ msepd->bNumEmbMIDIJack > 16)
+ return -ENODEV;
+
+ return create_any_midi_quirk(chip, iface, driver, NULL);
+}
+
+static int create_auto_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+ int err;
+
+ alts = &iface->altsetting[0];
+ altsd = get_iface_desc(alts);
+
+ /* must have at least one bulk/interrupt endpoint for streaming */
+ if (altsd->bNumEndpoints < 1)
+ return -ENODEV;
+ epd = get_endpoint(alts, 0);
+ if (!usb_endpoint_xfer_bulk(epd) ||
+ !usb_endpoint_xfer_int(epd))
+ return -ENODEV;
+
+ switch (USB_ID_VENDOR(chip->usb_id)) {
+ case 0x0499: /* Yamaha */
+ err = create_yamaha_midi_quirk(chip, iface, driver, alts);
+ if (err < 0 && err != -ENODEV)
+ return err;
+ break;
+ case 0x0582: /* Roland */
+ err = create_roland_midi_quirk(chip, iface, driver, alts);
+ if (err < 0 && err != -ENODEV)
+ return err;
+ break;
+ }
+
+ return create_std_midi_quirk(chip, iface, driver, alts);
+}
+
+static int create_autodetect_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ int err;
+
+ err = create_auto_pcm_quirk(chip, iface, driver);
+ if (err == -ENODEV)
+ err = create_auto_midi_quirk(chip, iface, driver);
+ return err;
+}
+
+static int create_autodetect_quirks(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
+ int ifcount, ifnum, err;
+
+ err = create_autodetect_quirk(chip, iface, driver);
+ if (err < 0)
+ return err;
+
+ /*
+ * ALSA PCM playback/capture devices cannot be registered in two steps,
+ * so we have to claim the other corresponding interface here.
+ */
+ ifcount = chip->dev->actconfig->desc.bNumInterfaces;
+ for (ifnum = 0; ifnum < ifcount; ifnum++) {
+ if (ifnum == probed_ifnum || quirk->ifnum >= 0)
+ continue;
+ iface = usb_ifnum_to_if(chip->dev, ifnum);
+ if (!iface ||
+ usb_interface_claimed(iface) ||
+ get_iface_desc(iface->altsetting)->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC)
+ continue;
+
+ err = create_autodetect_quirk(chip, iface, driver);
+ if (err >= 0)
+ usb_driver_claim_interface(driver, iface, (void *)-1L);
+ }
+
+ return 0;
+}
+
/*
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
* The only way to detect the sample rate is by looking at wMaxPacketSize.
@@ -303,9 +510,11 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
static const quirk_func_t quirk_funcs[] = {
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
[QUIRK_COMPOSITE] = create_composite_quirk,
+ [QUIRK_AUTODETECT] = create_autodetect_quirks,
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
+ [QUIRK_MIDI_ROLAND] = create_any_midi_quirk,
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
[QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 7db2f8958e79..c4339f97226b 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -493,10 +493,10 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
altsd = get_iface_desc(alts);
protocol = altsd->bInterfaceProtocol;
/* skip invalid one */
- if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
+ if (((altsd->bInterfaceClass != USB_CLASS_AUDIO ||
+ (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
+ altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
- (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
- altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
altsd->bNumEndpoints < 1 ||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
continue;
@@ -512,6 +512,15 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
continue;
+ /*
+ * Roland audio streaming interfaces are marked with protocols
+ * 0/1/2, but are UAC 1 compatible.
+ */
+ if (USB_ID_VENDOR(chip->usb_id) == 0x0582 &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ protocol <= 2)
+ protocol = UAC_VERSION_1;
+
chconfig = 0;
/* get audio formats */
switch (protocol) {
@@ -635,6 +644,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+ fp->protocol = protocol;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
fp->channels = num_channels;
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
@@ -676,7 +686,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
}
/* ok, let's parse further... */
- if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
+ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index bc43bcaddf4d..caabe9b3af49 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -72,9 +72,11 @@ struct snd_usb_audio {
enum quirk_type {
QUIRK_IGNORE_INTERFACE,
QUIRK_COMPOSITE,
+ QUIRK_AUTODETECT,
QUIRK_MIDI_STANDARD_INTERFACE,
QUIRK_MIDI_FIXED_ENDPOINT,
QUIRK_MIDI_YAMAHA,
+ QUIRK_MIDI_ROLAND,
QUIRK_MIDI_MIDIMAN,
QUIRK_MIDI_NOVATION,
QUIRK_MIDI_RAW_BYTES,
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 9af7c1f17413..1f9bbd55553f 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -150,7 +150,7 @@
MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}");
+MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604),"NAME_ALLCAPS"(0x8001)(0x8005)(0x8007)}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index b37653247ef4..4967fe9c938d 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -695,9 +695,6 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate)
((char*)(usbdata + i))[1] = ra[i].c2;
usb_fill_bulk_urb(us->urb[i], usX2Y->dev, usb_sndbulkpipe(usX2Y->dev, 4),
usbdata + i, 2, i_usX2Y_04Int, usX2Y);
-#ifdef OLD_USB
- us->urb[i]->transfer_flags = USB_QUEUE_BULK;
-#endif
}
us->submitted = 0;
us->len = NOOF_SETRATE_URBS;