aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/atmel/ac97c.c2
-rw-r--r--sound/core/jack.c5
-rw-r--r--sound/core/pcm_lib.c18
-rw-r--r--sound/core/pcm_native.c12
-rw-r--r--sound/core/sound_oss.c6
-rw-r--r--sound/drivers/aloop.c62
-rw-r--r--sound/firewire/amdtp.c49
-rw-r--r--sound/firewire/amdtp.h29
-rw-r--r--sound/pci/Kconfig2
-rw-r--r--sound/pci/ad1889.c15
-rw-r--r--sound/pci/ali5451/ali5451.c15
-rw-r--r--sound/pci/als300.c15
-rw-r--r--sound/pci/als4000.c15
-rw-r--r--sound/pci/atiixp.c16
-rw-r--r--sound/pci/atiixp_modem.c16
-rw-r--r--sound/pci/au88x0/au88x0.c17
-rw-r--r--sound/pci/aw2/aw2-alsa.c23
-rw-r--r--sound/pci/azt3328.c23
-rw-r--r--sound/pci/bt87x.c19
-rw-r--r--sound/pci/ca0106/ca0106_main.c17
-rw-r--r--sound/pci/cmipci.c15
-rw-r--r--sound/pci/cs4281.c15
-rw-r--r--sound/pci/cs46xx/cs46xx.c15
-rw-r--r--sound/pci/cs5530.c16
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c15
-rw-r--r--sound/pci/ctxfi/xfi.c13
-rw-r--r--sound/pci/echoaudio/echoaudio.c22
-rw-r--r--sound/pci/emu10k1/emu10k1.c15
-rw-r--r--sound/pci/emu10k1/emu10k1x.c17
-rw-r--r--sound/pci/ens1370.c15
-rw-r--r--sound/pci/es1938.c15
-rw-r--r--sound/pci/es1968.c15
-rw-r--r--sound/pci/fm801.c15
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_auto_parser.c760
-rw-r--r--sound/pci/hda/hda_auto_parser.h160
-rw-r--r--sound/pci/hda/hda_codec.c1027
-rw-r--r--sound/pci/hda/hda_codec.h15
-rw-r--r--sound/pci/hda/hda_intel.c57
-rw-r--r--sound/pci/hda/hda_jack.c1
-rw-r--r--sound/pci/hda/hda_jack.h2
-rw-r--r--sound/pci/hda/hda_local.h122
-rw-r--r--sound/pci/hda/patch_analog.c14
-rw-r--r--sound/pci/hda/patch_ca0110.c8
-rw-r--r--sound/pci/hda/patch_ca0132.c9
-rw-r--r--sound/pci/hda/patch_cirrus.c30
-rw-r--r--sound/pci/hda/patch_cmedia.c1
-rw-r--r--sound/pci/hda/patch_conexant.c186
-rw-r--r--sound/pci/hda/patch_hdmi.c4
-rw-r--r--sound/pci/hda/patch_realtek.c465
-rw-r--r--sound/pci/hda/patch_sigmatel.c120
-rw-r--r--sound/pci/hda/patch_via.c33
-rw-r--r--sound/pci/ice1712/ice1712.c15
-rw-r--r--sound/pci/ice1712/ice1724.c15
-rw-r--r--sound/pci/intel8x0.c16
-rw-r--r--sound/pci/intel8x0m.c16
-rw-r--r--sound/pci/korg1212/korg1212.c15
-rw-r--r--sound/pci/lola/lola.c15
-rw-r--r--sound/pci/lx6464es/lx6464es.c17
-rw-r--r--sound/pci/maestro3.c15
-rw-r--r--sound/pci/mixart/mixart.c15
-rw-r--r--sound/pci/nm256/nm256.c16
-rw-r--r--sound/pci/oxygen/oxygen.c21
-rw-r--r--sound/pci/oxygen/virtuoso.c13
-rw-r--r--sound/pci/oxygen/xonar_dg.c7
-rw-r--r--sound/pci/pcxhr/pcxhr.c15
-rw-r--r--sound/pci/riptide/riptide.c3
-rw-r--r--sound/pci/rme32.c15
-rw-r--r--sound/pci/rme96.c15
-rw-r--r--sound/pci/rme9652/hdsp.c15
-rw-r--r--sound/pci/rme9652/hdspm.c16
-rw-r--r--sound/pci/rme9652/rme9652.c15
-rw-r--r--sound/pci/sis7019.c13
-rw-r--r--sound/pci/sonicvibes.c15
-rw-r--r--sound/pci/trident/trident.c15
-rw-r--r--sound/pci/via82xx.c15
-rw-r--r--sound/pci/via82xx_modem.c15
-rw-r--r--sound/pci/vx222/vx222.c15
-rw-r--r--sound/pci/ymfpci/ymfpci.c15
-rw-r--r--sound/sh/sh_dac_audio.c4
-rw-r--r--sound/soc/Kconfig5
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c37
-rw-r--r--sound/soc/codecs/Kconfig20
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/ac97.c6
-rw-r--r--sound/soc/codecs/ad1836.c4
-rw-r--r--sound/soc/codecs/ad193x.c4
-rw-r--r--sound/soc/codecs/adau1701.c3
-rw-r--r--sound/soc/codecs/ak4104.c3
-rw-r--r--sound/soc/codecs/ak4535.c3
-rw-r--r--sound/soc/codecs/ak4641.c113
-rw-r--r--sound/soc/codecs/alc5623.c23
-rw-r--r--sound/soc/codecs/alc5632.c31
-rw-r--r--sound/soc/codecs/cs4270.c11
-rw-r--r--sound/soc/codecs/cs4271.c3
-rw-r--r--sound/soc/codecs/cs42l51.c9
-rw-r--r--sound/soc/codecs/cs42l52.c1295
-rw-r--r--sound/soc/codecs/cs42l52.h274
-rw-r--r--sound/soc/codecs/cs42l73.c93
-rw-r--r--sound/soc/codecs/da7210.c379
-rw-r--r--sound/soc/codecs/jz4740.c3
-rw-r--r--sound/soc/codecs/lm49453.c1550
-rw-r--r--sound/soc/codecs/lm49453.h380
-rw-r--r--sound/soc/codecs/max98095.c158
-rw-r--r--sound/soc/codecs/max98095.h22
-rw-r--r--sound/soc/codecs/mc13783.c786
-rw-r--r--sound/soc/codecs/mc13783.h28
-rw-r--r--sound/soc/codecs/ml26124.c681
-rw-r--r--sound/soc/codecs/ml26124.h184
-rw-r--r--sound/soc/codecs/omap-hdmi.c69
-rw-r--r--sound/soc/codecs/rt5631.c110
-rw-r--r--sound/soc/codecs/sgtl5000.c25
-rw-r--r--sound/soc/codecs/ssm2602.c138
-rw-r--r--sound/soc/codecs/sta32x.c3
-rw-r--r--sound/soc/codecs/tlv320aic23.c13
-rw-r--r--sound/soc/codecs/tlv320aic26.c3
-rw-r--r--sound/soc/codecs/tlv320aic3x.c21
-rw-r--r--sound/soc/codecs/tlv320dac33.c35
-rw-r--r--sound/soc/codecs/twl4030.c18
-rw-r--r--sound/soc/codecs/twl6040.c450
-rw-r--r--sound/soc/codecs/uda134x.c6
-rw-r--r--sound/soc/codecs/uda1380.c6
-rw-r--r--sound/soc/codecs/wl1273.c6
-rw-r--r--sound/soc/codecs/wm1250-ev1.c65
-rw-r--r--sound/soc/codecs/wm5100-tables.c125
-rw-r--r--sound/soc/codecs/wm5100.c47
-rw-r--r--sound/soc/codecs/wm5100.h159
-rw-r--r--sound/soc/codecs/wm8350.c187
-rw-r--r--sound/soc/codecs/wm8400.c135
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c3
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731.c37
-rw-r--r--sound/soc/codecs/wm8737.c3
-rw-r--r--sound/soc/codecs/wm8741.c3
-rw-r--r--sound/soc/codecs/wm8750.c3
-rw-r--r--sound/soc/codecs/wm8753.c6
-rw-r--r--sound/soc/codecs/wm8900.c3
-rw-r--r--sound/soc/codecs/wm8903.c3
-rw-r--r--sound/soc/codecs/wm8940.c3
-rw-r--r--sound/soc/codecs/wm8960.c3
-rw-r--r--sound/soc/codecs/wm8962.c18
-rw-r--r--sound/soc/codecs/wm8971.c3
-rw-r--r--sound/soc/codecs/wm8978.c3
-rw-r--r--sound/soc/codecs/wm8988.c3
-rw-r--r--sound/soc/codecs/wm8990.c3
-rw-r--r--sound/soc/codecs/wm8993.c86
-rw-r--r--sound/soc/codecs/wm8994.c290
-rw-r--r--sound/soc/codecs/wm8994.h3
-rw-r--r--sound/soc/codecs/wm8996.c12
-rw-r--r--sound/soc/codecs/wm9081.c5
-rw-r--r--sound/soc/codecs/wm9705.c6
-rw-r--r--sound/soc/codecs/wm9712.c10
-rw-r--r--sound/soc/codecs/wm_hubs.c220
-rw-r--r--sound/soc/codecs/wm_hubs.h12
-rw-r--r--sound/soc/ep93xx/ep93xx-ac97.c74
-rw-r--r--sound/soc/ep93xx/ep93xx-i2s.c49
-rw-r--r--sound/soc/fsl/Kconfig129
-rw-r--r--sound/soc/fsl/Makefile31
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c (renamed from sound/soc/imx/eukrea-tlv320.c)2
-rw-r--r--sound/soc/fsl/fsl_ssi.c167
-rw-r--r--sound/soc/fsl/fsl_utils.c91
-rw-r--r--sound/soc/fsl/fsl_utils.h26
-rw-r--r--sound/soc/fsl/imx-audmux.c (renamed from sound/soc/imx/imx-audmux.c)0
-rw-r--r--sound/soc/fsl/imx-audmux.h (renamed from sound/soc/imx/imx-audmux.h)0
-rw-r--r--sound/soc/fsl/imx-mc13783.c156
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c (renamed from sound/soc/imx/imx-pcm-dma-mx2.c)3
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c (renamed from sound/soc/imx/imx-pcm-fiq.c)0
-rw-r--r--sound/soc/fsl/imx-pcm.c (renamed from sound/soc/imx/imx-pcm.c)0
-rw-r--r--sound/soc/fsl/imx-pcm.h (renamed from sound/soc/imx/imx-pcm.h)1
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c221
-rw-r--r--sound/soc/fsl/imx-ssi.c (renamed from sound/soc/imx/imx-ssi.c)2
-rw-r--r--sound/soc/fsl/imx-ssi.h (renamed from sound/soc/imx/imx-ssi.h)0
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c166
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c (renamed from sound/soc/imx/mx27vis-aic32x4.c)0
-rw-r--r--sound/soc/fsl/p1022_ds.c158
-rw-r--r--sound/soc/fsl/phycore-ac97.c (renamed from sound/soc/imx/phycore-ac97.c)0
-rw-r--r--sound/soc/fsl/wm1133-ev1.c (renamed from sound/soc/imx/wm1133-ev1.c)0
-rw-r--r--sound/soc/generic/Kconfig4
-rw-r--r--sound/soc/generic/Makefile3
-rw-r--r--sound/soc/generic/simple-card.c114
-rw-r--r--sound/soc/imx/Kconfig79
-rw-r--r--sound/soc/imx/Makefile22
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c4
-rw-r--r--sound/soc/mxs/mxs-pcm.c24
-rw-r--r--sound/soc/mxs/mxs-pcm.h3
-rw-r--r--sound/soc/mxs/mxs-saif.c92
-rw-r--r--sound/soc/mxs/mxs-saif.h1
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c50
-rw-r--r--sound/soc/omap/Kconfig1
-rw-r--r--sound/soc/pxa/pxa-ssp.c28
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c4
-rw-r--r--sound/soc/samsung/littlemill.c102
-rw-r--r--sound/soc/samsung/lowland.c75
-rw-r--r--sound/soc/samsung/speyside.c33
-rw-r--r--sound/soc/sh/Kconfig24
-rw-r--r--sound/soc/sh/Makefile6
-rw-r--r--sound/soc/sh/fsi-ak4642.c108
-rw-r--r--sound/soc/sh/fsi-da7210.c81
-rw-r--r--sound/soc/sh/fsi-hdmi.c118
-rw-r--r--sound/soc/sh/fsi.c224
-rw-r--r--sound/soc/soc-core.c690
-rw-r--r--sound/soc/soc-dapm.c562
-rw-r--r--sound/soc/soc-jack.c5
-rw-r--r--sound/soc/soc-pcm.c1718
-rw-r--r--sound/soc/tegra/Kconfig68
-rw-r--r--sound/soc/tegra/Makefile20
-rw-r--r--sound/soc/tegra/tegra20_das.c233
-rw-r--r--sound/soc/tegra/tegra20_das.h134
-rw-r--r--sound/soc/tegra/tegra20_i2s.c494
-rw-r--r--sound/soc/tegra/tegra20_i2s.h164
-rw-r--r--sound/soc/tegra/tegra20_spdif.c404
-rw-r--r--sound/soc/tegra/tegra20_spdif.h471
-rw-r--r--sound/soc/tegra/tegra30_ahub.c631
-rw-r--r--sound/soc/tegra/tegra30_ahub.h483
-rw-r--r--sound/soc/tegra/tegra30_i2s.c536
-rw-r--r--sound/soc/tegra/tegra30_i2s.h242
-rw-r--r--sound/soc/tegra/tegra_alc5632.c48
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c37
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h9
-rw-r--r--sound/soc/tegra/tegra_das.c261
-rw-r--r--sound/soc/tegra/tegra_das.h135
-rw-r--r--sound/soc/tegra/tegra_i2s.c459
-rw-r--r--sound/soc/tegra/tegra_i2s.h166
-rw-r--r--sound/soc/tegra/tegra_pcm.c28
-rw-r--r--sound/soc/tegra/tegra_pcm.h5
-rw-r--r--sound/soc/tegra/tegra_spdif.c364
-rw-r--r--sound/soc/tegra/tegra_spdif.h473
-rw-r--r--sound/soc/tegra/tegra_wm8753.c224
-rw-r--r--sound/soc/tegra/tegra_wm8903.c29
-rw-r--r--sound/soc/tegra/trimslice.c41
-rw-r--r--sound/soc/ux500/Kconfig14
-rw-r--r--sound/soc/ux500/Makefile4
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c843
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h79
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c742
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h553
-rw-r--r--sound/sound_core.c2
-rw-r--r--sound/usb/card.c10
-rw-r--r--sound/usb/card.h86
-rw-r--r--sound/usb/endpoint.c1609
-rw-r--r--sound/usb/endpoint.h32
-rw-r--r--sound/usb/mixer.c50
-rw-r--r--sound/usb/mixer.h3
-rw-r--r--sound/usb/mixer_maps.c13
-rw-r--r--sound/usb/mixer_quirks.c472
-rw-r--r--sound/usb/pcm.c453
-rw-r--r--sound/usb/proc.c38
-rw-r--r--sound/usb/stream.c31
-rw-r--r--sound/usb/usbaudio.h2
251 files changed, 21166 insertions, 7963 deletions
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 115313ef54d..f5ded640b39 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -991,6 +991,8 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
gpio_direction_output(pdata->reset_pin, 1);
chip->reset_pin = pdata->reset_pin;
}
+ } else {
+ chip->reset_pin = -EINVAL;
}
snd_card_set_dev(card, &pdev->dev);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 471e1e3b0a9..a06b1651fcb 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -155,7 +155,7 @@ EXPORT_SYMBOL(snd_jack_new);
* @jack: The jack to configure
* @parent: The device to set as parent for the jack.
*
- * Set the parent for the jack input device in the device tree. This
+ * Set the parent for the jack devices in the device tree. This
* function is only valid prior to registration of the jack. If no
* parent is configured then the parent device will be the sound card.
*/
@@ -179,6 +179,9 @@ EXPORT_SYMBOL(snd_jack_set_parent);
* mapping is provided but keys are enabled in the jack type then
* BTN_n numeric buttons will be reported.
*
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
* Note that this is intended to be use by simple devices with small
* numbers of keys that can be reported. It is also possible to
* access the input device directly - devices with complex input
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 4d18941178e..faedb1481b2 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1894,6 +1894,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
snd_pcm_uframes_t offset = 0;
+ snd_pcm_uframes_t avail;
int err = 0;
if (size == 0)
@@ -1917,13 +1918,12 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
}
runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+ avail = snd_pcm_playback_avail(runtime);
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_playback_avail(runtime);
if (!avail) {
if (nonblock) {
err = -EAGAIN;
@@ -1971,6 +1971,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
offset += frames;
size -= frames;
xfer += frames;
+ avail -= frames;
if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
err = snd_pcm_start(substream);
@@ -2111,6 +2112,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
snd_pcm_uframes_t offset = 0;
+ snd_pcm_uframes_t avail;
int err = 0;
if (size == 0)
@@ -2141,13 +2143,12 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
}
runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+ avail = snd_pcm_capture_avail(runtime);
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_capture_avail(runtime);
if (!avail) {
if (runtime->status->state ==
SNDRV_PCM_STATE_DRAINING) {
@@ -2202,6 +2203,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
offset += frames;
size -= frames;
xfer += frames;
+ avail -= frames;
}
_end_unlock:
runtime->twake = 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 3fe99e644eb..53b5ada8f7c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1360,7 +1360,14 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
{
- substream->runtime->trigger_master = substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ switch (runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBADFD;
+ }
+ runtime->trigger_master = substream;
return 0;
}
@@ -1379,6 +1386,9 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
case SNDRV_PCM_STATE_RUNNING:
runtime->status->state = SNDRV_PCM_STATE_DRAINING;
break;
+ case SNDRV_PCM_STATE_XRUN:
+ runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ break;
default:
break;
}
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index c7009204306..e9528333e36 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -35,7 +35,7 @@
#include <linux/sound.h>
#include <linux/mutex.h>
-#define SNDRV_OSS_MINORS 128
+#define SNDRV_OSS_MINORS 256
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
static DEFINE_MUTEX(sound_oss_mutex);
@@ -111,7 +111,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
int register1 = -1, register2 = -1;
struct device *carddev = snd_card_get_device_link(card);
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0; /* ignore silently */
if (minor < 0)
return minor;
@@ -170,7 +170,7 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
int track2 = -1;
struct snd_minor *mptr;
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0;
if (minor < 0)
return minor;
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index ad079b63b8b..8b5c36f4d30 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -117,6 +117,7 @@ struct loopback_pcm {
/* timer stuff */
unsigned int irq_pos; /* fractional IRQ position */
unsigned int period_size_frac;
+ unsigned int last_drift;
unsigned long last_jiffies;
struct timer_list timer;
};
@@ -264,6 +265,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
dpcm->last_jiffies = jiffies;
dpcm->pcm_rate_shift = 0;
+ dpcm->last_drift = 0;
spin_lock(&cable->lock);
cable->running |= stream;
cable->pause &= ~stream;
@@ -444,34 +446,30 @@ static void copy_play_buf(struct loopback_pcm *play,
}
}
-#define BYTEPOS_UPDATE_POSONLY 0
-#define BYTEPOS_UPDATE_CLEAR 1
-#define BYTEPOS_UPDATE_COPY 2
-
-static void loopback_bytepos_update(struct loopback_pcm *dpcm,
- unsigned int delta,
- unsigned int cmd)
+static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
+ unsigned int jiffies_delta)
{
- unsigned int count;
unsigned long last_pos;
+ unsigned int delta;
last_pos = byte_pos(dpcm, dpcm->irq_pos);
- dpcm->irq_pos += delta * dpcm->pcm_bps;
- count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
- if (!count)
- return;
- if (cmd == BYTEPOS_UPDATE_CLEAR)
- clear_capture_buf(dpcm, count);
- else if (cmd == BYTEPOS_UPDATE_COPY)
- copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- count);
- dpcm->buf_pos += count;
- dpcm->buf_pos %= dpcm->pcm_buffer_size;
+ dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
+ delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+ if (delta >= dpcm->last_drift)
+ delta -= dpcm->last_drift;
+ dpcm->last_drift = 0;
if (dpcm->irq_pos >= dpcm->period_size_frac) {
dpcm->irq_pos %= dpcm->period_size_frac;
dpcm->period_update_pending = 1;
}
+ return delta;
+}
+
+static inline void bytepos_finish(struct loopback_pcm *dpcm,
+ unsigned int delta)
+{
+ dpcm->buf_pos += delta;
+ dpcm->buf_pos %= dpcm->pcm_buffer_size;
}
static unsigned int loopback_pos_update(struct loopback_cable *cable)
@@ -481,7 +479,7 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
struct loopback_pcm *dpcm_capt =
cable->streams[SNDRV_PCM_STREAM_CAPTURE];
unsigned long delta_play = 0, delta_capt = 0;
- unsigned int running;
+ unsigned int running, count1, count2;
unsigned long flags;
spin_lock_irqsave(&cable->lock, flags);
@@ -500,12 +498,13 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
goto unlock;
if (delta_play > delta_capt) {
- loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
- BYTEPOS_UPDATE_POSONLY);
+ count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
+ bytepos_finish(dpcm_play, count1);
delta_play = delta_capt;
} else if (delta_play < delta_capt) {
- loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
- BYTEPOS_UPDATE_CLEAR);
+ count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
+ clear_capture_buf(dpcm_capt, count1);
+ bytepos_finish(dpcm_capt, count1);
delta_capt = delta_play;
}
@@ -513,8 +512,17 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
goto unlock;
/* note delta_capt == delta_play at this moment */
- loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
- loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+ count1 = bytepos_delta(dpcm_play, delta_play);
+ count2 = bytepos_delta(dpcm_capt, delta_capt);
+ if (count1 < count2) {
+ dpcm_capt->last_drift = count2 - count1;
+ count1 = count2;
+ } else if (count1 > count2) {
+ dpcm_play->last_drift = count1 - count2;
+ }
+ copy_play_buf(dpcm_play, dpcm_capt, count1);
+ bytepos_finish(dpcm_play, count1);
+ bytepos_finish(dpcm_capt, count1);
unlock:
spin_unlock_irqrestore(&cable->lock, flags);
return running;
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 87657dd7714..ea995af6d04 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -31,6 +31,8 @@
#define INTERRUPT_INTERVAL 16
#define QUEUE_LENGTH 48
+static void pcm_period_tasklet(unsigned long data);
+
/**
* amdtp_out_stream_init - initialize an AMDTP output stream structure
* @s: the AMDTP output stream to initialize
@@ -47,6 +49,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
s->flags = flags;
s->context = ERR_PTR(-1);
mutex_init(&s->mutex);
+ tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
s->packet_index = 0;
return 0;
@@ -164,6 +167,21 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
}
EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
+/**
+ * amdtp_out_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP output stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+{
+ tasklet_kill(&s->period_tasklet);
+ s->pcm_buffer_pointer = 0;
+ s->pcm_period_pointer = 0;
+ s->pointer_flush = true;
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+
static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
{
unsigned int phase, data_blocks;
@@ -376,11 +394,21 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
s->pcm_period_pointer += data_blocks;
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size;
- snd_pcm_period_elapsed(pcm);
+ s->pointer_flush = false;
+ tasklet_hi_schedule(&s->period_tasklet);
}
}
}
+static void pcm_period_tasklet(unsigned long data)
+{
+ struct amdtp_out_stream *s = (void *)data;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+ if (pcm)
+ snd_pcm_period_elapsed(pcm);
+}
+
static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header, void *data)
{
@@ -506,6 +534,24 @@ err_unlock:
EXPORT_SYMBOL(amdtp_out_stream_start);
/**
+ * amdtp_out_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP output stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+{
+ /* this optimization is allowed to be racy */
+ if (s->pointer_flush)
+ fw_iso_context_flush_completions(s->context);
+ else
+ s->pointer_flush = true;
+
+ return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+
+/**
* amdtp_out_stream_update - update the stream after a bus reset
* @s: the AMDTP output stream
*/
@@ -532,6 +578,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
return;
}
+ tasklet_kill(&s->period_tasklet);
fw_iso_context_stop(s->context);
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 537a9cb8358..b680c5ef01d 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -1,6 +1,7 @@
#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include "packets-buffer.h"
@@ -55,6 +56,7 @@ struct amdtp_out_stream {
struct iso_packets_buffer buffer;
struct snd_pcm_substream *pcm;
+ struct tasklet_struct period_tasklet;
int packet_index;
unsigned int data_block_counter;
@@ -66,6 +68,7 @@ struct amdtp_out_stream {
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
+ bool pointer_flush;
};
int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
@@ -81,6 +84,8 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s);
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
snd_pcm_format_t format);
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
/**
@@ -123,18 +128,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
}
/**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
-{
- s->pcm_buffer_pointer = 0;
- s->pcm_period_pointer = 0;
-}
-
-/**
* amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
* @s: the AMDTP output stream
* @pcm: the PCM device to be started, or %NULL to stop the current device
@@ -149,18 +142,6 @@ static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
ACCESS_ONCE(s->pcm) = pcm;
}
-/**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
- *
- * Returns the current buffer position, in frames.
- */
-static inline unsigned long
-amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
-{
- return ACCESS_ONCE(s->pcm_buffer_pointer);
-}
-
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 5ca0939e422..ff3af6e77d6 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -228,7 +228,7 @@ config SND_OXYGEN
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
* Asound A-8788
- * Asus Xonar DG
+ * Asus Xonar DG/DGX
* AuzenTech X-Meridian
* AuzenTech X-Meridian 2G
* Bgears b-Enspirer
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 9d91d61902b..e672ff4df2d 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -1062,17 +1062,4 @@ static struct pci_driver ad1889_pci_driver = {
.remove = __devexit_p(snd_ad1889_remove),
};
-static int __init
-alsa_ad1889_init(void)
-{
- return pci_register_driver(&ad1889_pci_driver);
-}
-
-static void __exit
-alsa_ad1889_fini(void)
-{
- pci_unregister_driver(&ad1889_pci_driver);
-}
-
-module_init(alsa_ad1889_init);
-module_exit(alsa_ad1889_fini);
+module_pci_driver(ad1889_pci_driver);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index bdd6164e9c7..9dfc27bf6cc 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2294,7 +2294,7 @@ static void __devexit snd_ali_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver ali5451_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ali_ids,
.probe = snd_ali_probe,
@@ -2305,15 +2305,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_ali_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ali_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ali_init)
-module_exit(alsa_card_ali_exit)
+module_pci_driver(ali5451_driver);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 8196e229b2d..59d65388faf 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -852,7 +852,7 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
return 0;
}
-static struct pci_driver driver = {
+static struct pci_driver als300_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
@@ -863,15 +863,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_als300_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als300_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als300_init)
-module_exit(alsa_card_als300_exit)
+module_pci_driver(als300_driver);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 3269b8011ea..7d7f2598c74 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -1036,7 +1036,7 @@ static int snd_als4000_resume(struct pci_dev *pci)
#endif /* CONFIG_PM */
-static struct pci_driver driver = {
+static struct pci_driver als4000_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_als4000_ids,
.probe = snd_card_als4000_probe,
@@ -1047,15 +1047,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_als4000_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als4000_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als4000_init)
-module_exit(alsa_card_als4000_exit)
+module_pci_driver(als4000_driver);
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 590682f115e..156a94f8a12 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1700,7 +1700,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver atiixp_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
@@ -1711,16 +1711,4 @@ static struct pci_driver driver = {
#endif
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_driver);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 524d35f3123..30a4fd96ce7 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1331,7 +1331,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver atiixp_modem_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
@@ -1342,16 +1342,4 @@ static struct pci_driver driver = {
#endif
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_modem_driver);
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index f13ad536b2d..ffc376f9f4e 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -375,24 +375,11 @@ static void __devexit snd_vortex_remove(struct pci_dev *pci)
}
// pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver vortex_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vortex_ids,
.probe = snd_vortex_probe,
.remove = __devexit_p(snd_vortex_remove),
};
-// initialization of the module
-static int __init alsa_card_vortex_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_vortex_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vortex_init)
-module_exit(alsa_card_vortex_exit)
+module_pci_driver(vortex_driver);
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 1c523193146..0f804741825 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -112,8 +112,6 @@ struct aw2 {
/*********************************
* FUNCTION DECLARATIONS
********************************/
-static int __init alsa_card_aw2_init(void);
-static void __exit alsa_card_aw2_exit(void);
static int snd_aw2_dev_free(struct snd_device *device);
static int __devinit snd_aw2_create(struct snd_card *card,
struct pci_dev *pci, struct aw2 **rchip);
@@ -171,13 +169,15 @@ static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver aw2_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
.remove = __devexit_p(snd_aw2_remove),
};
+module_pci_driver(aw2_driver);
+
/* operators for playback PCM alsa interface */
static struct snd_pcm_ops snd_aw2_playback_ops = {
.open = snd_aw2_pcm_playback_open,
@@ -217,23 +217,6 @@ static struct snd_kcontrol_new aw2_control __devinitdata = {
* FUNCTION IMPLEMENTATIONS
********************************/
-/* initialization of the module */
-static int __init alsa_card_aw2_init(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
- return pci_register_driver(&driver);
-}
-
-/* clean up the module */
-static void __exit alsa_card_aw2_exit(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_aw2_init);
-module_exit(alsa_card_aw2_exit);
-
/* component-destructor */
static int snd_aw2_dev_free(struct snd_device *device)
{
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 496f14c1a73..f0b4d7493af 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2862,7 +2862,7 @@ snd_azf3328_resume(struct pci_dev *pci)
#endif /* CONFIG_PM */
-static struct pci_driver driver = {
+static struct pci_driver azf3328_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
@@ -2873,23 +2873,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init
-alsa_card_azf3328_init(void)
-{
- int err;
- snd_azf3328_dbgcallenter();
- err = pci_register_driver(&driver);
- snd_azf3328_dbgcallleave();
- return err;
-}
-
-static void __exit
-alsa_card_azf3328_exit(void)
-{
- snd_azf3328_dbgcallenter();
- pci_unregister_driver(&driver);
- snd_azf3328_dbgcallleave();
-}
-
-module_init(alsa_card_azf3328_init)
-module_exit(alsa_card_azf3328_exit)
+module_pci_driver(azf3328_driver);
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 62d6163fc9d..b6a95eeca09 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -836,8 +836,6 @@ static struct {
{0x7063, 0x2000}, /* pcHDTV HD-2000 TV */
};
-static struct pci_driver driver;
-
/* return the id of the card, or a negative value if it's blacklisted */
static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
{
@@ -964,24 +962,11 @@ static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
{ }
};
-static struct pci_driver driver = {
+static struct pci_driver bt87x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_bt87x_ids,
.probe = snd_bt87x_probe,
.remove = __devexit_p(snd_bt87x_remove),
};
-static int __init alsa_card_bt87x_init(void)
-{
- if (load_all)
- driver.id_table = snd_bt87x_default_ids;
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_bt87x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_bt87x_init)
-module_exit(alsa_card_bt87x_exit)
+module_pci_driver(bt87x_driver);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 08d6ebfe5a6..e76d68a7081 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1932,7 +1932,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = {
MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
// pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver ca0106_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
@@ -1943,17 +1943,4 @@ static struct pci_driver driver = {
#endif
};
-// initialization of the module
-static int __init alsa_card_ca0106_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_ca0106_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ca0106_init)
-module_exit(alsa_card_ca0106_exit)
+module_pci_driver(ca0106_driver);
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 19b06269adc..3815bd4c677 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -3398,7 +3398,7 @@ static int snd_cmipci_resume(struct pci_dev *pci)
}
#endif /* CONFIG_PM */
-static struct pci_driver driver = {
+static struct pci_driver cmipci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
@@ -3409,15 +3409,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_cmipci_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cmipci_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cmipci_init)
-module_exit(alsa_card_cmipci_exit)
+module_pci_driver(cmipci_driver);
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index a9f368f60df..33506ee569b 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -2084,7 +2084,7 @@ static int cs4281_resume(struct pci_dev *pci)
}
#endif /* CONFIG_PM */
-static struct pci_driver driver = {
+static struct pci_driver cs4281_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs4281_ids,
.probe = snd_cs4281_probe,
@@ -2095,15 +2095,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_cs4281_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs4281_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs4281_init)
-module_exit(alsa_card_cs4281_exit)
+module_pci_driver(cs4281_driver);
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 819d79d0586..6cc7404e0e8 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -161,7 +161,7 @@ static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver cs46xx_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
@@ -172,15 +172,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_cs46xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs46xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs46xx_init)
-module_exit(alsa_card_cs46xx_exit)
+module_pci_driver(cs46xx_driver);
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index c47cabff2bf..f1e4229993a 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -291,23 +291,11 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci,
return 0;
}
-static struct pci_driver driver = {
+static struct pci_driver cs5530_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
.remove = __devexit_p(snd_cs5530_remove),
};
-static int __init alsa_card_cs5530_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5530_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5530_init)
-module_exit(alsa_card_cs5530_exit)
-
+module_pci_driver(cs5530_driver);
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index a2fb2173e98..2c9697cf0a1 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -394,7 +394,7 @@ static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver cs5535audio_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
@@ -405,18 +405,7 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_cs5535audio_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5535audio_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5535audio_init)
-module_exit(alsa_card_cs5535audio_exit)
+module_pci_driver(cs5535audio_driver);
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index 15d95d2bace..75aa2c33841 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -154,15 +154,4 @@ static struct pci_driver ct_driver = {
#endif
};
-static int __init ct_card_init(void)
-{
- return pci_register_driver(&ct_driver);
-}
-
-static void __exit ct_card_exit(void)
-{
- pci_unregister_driver(&ct_driver);
-}
-
-module_init(ct_card_init)
-module_exit(ct_card_exit)
+module_pci_driver(ct_driver);
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 595c11f904b..0f8eda1dafd 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2328,7 +2328,7 @@ static void __devexit snd_echo_remove(struct pci_dev *pci)
******************************************************************************/
/* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver echo_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_echo_ids,
.probe = snd_echo_probe,
@@ -2339,22 +2339,4 @@ static struct pci_driver driver = {
#endif /* CONFIG_PM */
};
-
-
-/* initialization of the module */
-static int __init alsa_card_echo_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-
-
-/* clean up the module */
-static void __exit alsa_card_echo_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-
-module_init(alsa_card_echo_init)
-module_exit(alsa_card_echo_exit)
+module_pci_driver(echo_driver);
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 790c65d980c..7fdbbe4d996 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -263,7 +263,7 @@ static int snd_emu10k1_resume(struct pci_dev *pci)
}
#endif
-static struct pci_driver driver = {
+static struct pci_driver emu10k1_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_emu10k1_ids,
.probe = snd_card_emu10k1_probe,
@@ -274,15 +274,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_emu10k1_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_emu10k1_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1_init)
-module_exit(alsa_card_emu10k1_exit)
+module_pci_driver(emu10k1_driver);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 47a651cb6e8..5c8978b2c4d 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1612,24 +1612,11 @@ static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = {
MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
// pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver emu10k1x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
.remove = __devexit_p(snd_emu10k1x_remove),
};
-// initialization of the module
-static int __init alsa_card_emu10k1x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_emu10k1x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1x_init)
-module_exit(alsa_card_emu10k1x_exit)
+module_pci_driver(emu10k1x_driver);
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 47a245e8419..3821c81d1c9 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -2488,7 +2488,7 @@ static void __devexit snd_audiopci_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver ens137x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_audiopci_ids,
.probe = snd_audiopci_probe,
@@ -2499,15 +2499,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_ens137x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ens137x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ens137x_init)
-module_exit(alsa_card_ens137x_exit)
+module_pci_driver(ens137x_driver);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 53eb76b4110..82c8d8c5c52 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1882,7 +1882,7 @@ static void __devexit snd_es1938_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver es1938_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_es1938_ids,
.probe = snd_es1938_probe,
@@ -1893,15 +1893,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_es1938_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1938_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1938_init)
-module_exit(alsa_card_es1938_exit)
+module_pci_driver(es1938_driver);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index a8faae1c85e..67f47d89195 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2898,7 +2898,7 @@ static void __devexit snd_es1968_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver es1968_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_es1968_ids,
.probe = snd_es1968_probe,
@@ -2909,15 +2909,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_es1968_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1968_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1968_init)
-module_exit(alsa_card_es1968_exit)
+module_pci_driver(es1968_driver);
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index a416ea8af3e..f6966232275 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1416,7 +1416,7 @@ static int snd_fm801_resume(struct pci_dev *pci)
}
#endif
-static struct pci_driver driver = {
+static struct pci_driver fm801_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
@@ -1427,15 +1427,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_fm801_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_fm801_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_fm801_init)
-module_exit(alsa_card_fm801_exit)
+module_pci_driver(fm801_driver);
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ace157cc3d1..bd4149f1aaf 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,6 +1,6 @@
snd-hda-intel-objs := hda_intel.o
-snd-hda-codec-y := hda_codec.o hda_jack.o
+snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644
index 00000000000..6e9ef3e2509
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -0,0 +1,760 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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 <linux/export.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+#define SFX "hda_codec: "
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
+ int num_pins)
+{
+ int i, j;
+ short seq;
+ hda_nid_t nid;
+
+ for (i = 0; i < num_pins; i++) {
+ for (j = i + 1; j < num_pins; j++) {
+ if (sequences[i] > sequences[j]) {
+ seq = sequences[i];
+ sequences[i] = sequences[j];
+ sequences[j] = seq;
+ nid = pins[i];
+ pins[i] = pins[j];
+ pins[j] = nid;
+ }
+ }
+ }
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+ int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->num_inputs++;
+ }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+ int i, j;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ for (j = i + 1; j < cfg->num_inputs; j++) {
+ if (cfg->inputs[i].type > cfg->inputs[j].type) {
+ struct auto_pin_cfg_item tmp;
+ tmp = cfg->inputs[i];
+ cfg->inputs[i] = cfg->inputs[j];
+ cfg->inputs[j] = tmp;
+ }
+ }
+ }
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ * 4-ch: front/surr => OK as it is
+ * 6-ch: front/clfe/surr
+ * 8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+ hda_nid_t nid;
+
+ switch (nums) {
+ case 3:
+ case 4:
+ nid = pins[1];
+ pins[1] = pins[2];
+ pins[2] = nid;
+ break;
+ }
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0]. So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags)
+{
+ hda_nid_t nid, end_nid;
+ short seq, assoc_line_out;
+ short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
+ short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+ short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ memset(sequences_line_out, 0, sizeof(sequences_line_out));
+ memset(sequences_speaker, 0, sizeof(sequences_speaker));
+ memset(sequences_hp, 0, sizeof(sequences_hp));
+ assoc_line_out = 0;
+
+ codec->ignore_misc_bit = true;
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wid_caps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wid_caps);
+ unsigned int def_conf;
+ short assoc, loc, conn, dev;
+
+ /* read all default configuration for pin complex */
+ if (wid_type != AC_WID_PIN)
+ continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+ continue;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+ AC_DEFCFG_MISC_NO_PRESENCE))
+ codec->ignore_misc_bit = false;
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ continue;
+ loc = get_defcfg_location(def_conf);
+ dev = get_defcfg_device(def_conf);
+
+ /* workaround for buggy BIOS setups */
+ if (dev == AC_JACK_LINE_OUT) {
+ if (conn == AC_JACK_PORT_FIXED)
+ dev = AC_JACK_SPEAKER;
+ }
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
+ if (!assoc)
+ continue;
+ if (!assoc_line_out)
+ assoc_line_out = assoc;
+ else if (assoc_line_out != assoc)
+ continue;
+ if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+ continue;
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ sequences_line_out[cfg->line_outs] = seq;
+ cfg->line_outs++;
+ break;
+ case AC_JACK_SPEAKER:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+ continue;
+ cfg->speaker_pins[cfg->speaker_outs] = nid;
+ sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
+ cfg->speaker_outs++;
+ break;
+ case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+ continue;
+ cfg->hp_pins[cfg->hp_outs] = nid;
+ sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
+ cfg->hp_outs++;
+ break;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+ break;
+ case AC_JACK_LINE_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+ break;
+ case AC_JACK_CD:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+ break;
+ case AC_JACK_AUX:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+ break;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+ continue;
+ cfg->dig_out_pins[cfg->dig_outs] = nid;
+ cfg->dig_out_type[cfg->dig_outs] =
+ (loc == AC_JACK_LOC_HDMI) ?
+ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+ cfg->dig_outs++;
+ break;
+ case AC_JACK_SPDIF_IN:
+ case AC_JACK_DIG_OTHER_IN:
+ cfg->dig_in_pin = nid;
+ if (loc == AC_JACK_LOC_HDMI)
+ cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+ else
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
+ }
+ }
+
+ /* FIX-UP:
+ * If no line-out is defined but multiple HPs are found,
+ * some of them might be the real line-outs.
+ */
+ if (!cfg->line_outs && cfg->hp_outs > 1 &&
+ !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+ int i = 0;
+ while (i < cfg->hp_outs) {
+ /* The real HPs should have the sequence 0x0f */
+ if ((sequences_hp[i] & 0x0f) == 0x0f) {
+ i++;
+ continue;
+ }
+ /* Move it to the line-out table */
+ cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+ sequences_line_out[cfg->line_outs] = sequences_hp[i];
+ cfg->line_outs++;
+ cfg->hp_outs--;
+ memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+ sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+ memmove(sequences_hp + i, sequences_hp + i + 1,
+ sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
+ }
+ memset(cfg->hp_pins + cfg->hp_outs, 0,
+ sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+ if (!cfg->hp_outs)
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+ }
+
+ /* sort by sequence */
+ sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
+ cfg->line_outs);
+ sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+ cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+ cfg->hp_outs);
+
+ /*
+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ * as a primary output
+ */
+ if (!cfg->line_outs &&
+ !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+ if (cfg->speaker_outs) {
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ } else if (cfg->hp_outs) {
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+ }
+
+ reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+ reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+ reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+ sort_autocfg_input_pins(cfg);
+
+ /*
+ * debug prints of the parsed results
+ */
+ snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+ cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[3],
+ cfg->line_out_pins[4],
+ cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+ (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+ "speaker" : "line"));
+ snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->speaker_outs, cfg->speaker_pins[0],
+ cfg->speaker_pins[1], cfg->speaker_pins[2],
+ cfg->speaker_pins[3], cfg->speaker_pins[4]);
+ snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->hp_outs, cfg->hp_pins[0],
+ cfg->hp_pins[1], cfg->hp_pins[2],
+ cfg->hp_pins[3], cfg->hp_pins[4]);
+ snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ if (cfg->dig_outs)
+ snd_printd(" dig-out=0x%x/0x%x\n",
+ cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+ snd_printd(" inputs:");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ snd_printd(" %s=0x%x",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ snd_printd("\n");
+ if (cfg->dig_in_pin)
+ snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
+
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+ hda_nid_t pin, bool check_location)
+{
+ unsigned int def_conf;
+ static const char * const mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ };
+ int attr;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (!check_location)
+ return "Mic";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ default:
+ return "Misc";
+ }
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+ int num_pins, int *indexp)
+{
+ static const char * const channel_sfx[] = {
+ " Front", " Surround", " CLFE", " Side"
+ };
+ int i;
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return NULL;
+ if (num_pins == 1)
+ return "";
+ if (num_pins > ARRAY_SIZE(channel_sfx)) {
+ if (indexp)
+ *indexp = i;
+ return "";
+ }
+ return channel_sfx[i];
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *name, char *label, int maxlen,
+ int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+ const char *pfx = "", *sfx = "";
+
+ /* handle as a speaker if it's a fixed line-out */
+ if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+ name = "Speaker";
+ /* check the location */
+ switch (attr) {
+ case INPUT_PIN_ATTR_DOCK:
+ pfx = "Dock ";
+ break;
+ case INPUT_PIN_ATTR_FRONT:
+ pfx = "Front ";
+ break;
+ }
+ if (cfg) {
+ /* try to give a unique suffix if needed */
+ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+ indexp);
+ if (!sfx)
+ sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+ indexp);
+ if (!sfx) {
+ /* don't add channel suffix for Headphone controls */
+ int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
+ cfg->hp_outs);
+ if (idx >= 0)
+ *indexp = idx;
+ sfx = "";
+ }
+ }
+ snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+ return 1;
+}
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin. This function works for both input and
+ * output pins. When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible. For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ const char *name = NULL;
+ int i;
+
+ if (indexp)
+ *indexp = 0;
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ return 0;
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Line Out",
+ label, maxlen, indexp);
+ case AC_JACK_SPEAKER:
+ return fill_audio_out_name(codec, nid, cfg, "Speaker",
+ label, maxlen, indexp);
+ case AC_JACK_HP_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Headphone",
+ label, maxlen, indexp);
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+ name = "HDMI";
+ else
+ name = "SPDIF";
+ if (cfg && indexp) {
+ i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+ cfg->dig_outs);
+ if (i >= 0)
+ *indexp = i;
+ }
+ break;
+ default:
+ if (cfg) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ name = hda_get_autocfg_input_label(codec, cfg, i);
+ if (name)
+ break;
+ }
+ }
+ if (!name)
+ name = hda_get_input_pin_label(codec, nid, true);
+ break;
+ }
+ if (!name)
+ return 0;
+ strlcpy(label, name, maxlen);
+ return 1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+ const struct hda_verb *list)
+{
+ const struct hda_verb **v;
+ snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+ v = snd_array_new(&spec->verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = list;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
+
+void snd_hda_gen_apply_verbs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ for (i = 0; i < spec->verbs.used; i++) {
+ struct hda_verb **v = snd_array_elem(&spec->verbs, i);
+ snd_hda_sequence_write(codec, *v);
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
+
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
+
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int id = spec->fixup_id;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *modelname = spec->fixup_name;
+#endif
+ int depth = 0;
+
+ if (!spec->fixup_list)
+ return;
+
+ while (id >= 0) {
+ const struct hda_fixup *fix = spec->fixup_list + id;
+
+ switch (fix->type) {
+ case HDA_FIXUP_PINS:
+ if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+ break;
+ snd_printdd(KERN_INFO SFX
+ "%s: Apply pincfg for %s\n",
+ codec->chip_name, modelname);
+ snd_hda_apply_pincfgs(codec, fix->v.pins);
+ break;
+ case HDA_FIXUP_VERBS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+ break;
+ snd_printdd(KERN_INFO SFX
+ "%s: Apply fix-verbs for %s\n",
+ codec->chip_name, modelname);
+ snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
+ break;
+ case HDA_FIXUP_FUNC:
+ if (!fix->v.func)
+ break;
+ snd_printdd(KERN_INFO SFX
+ "%s: Apply fix-func for %s\n",
+ codec->chip_name, modelname);
+ fix->v.func(codec, fix, action);
+ break;
+ default:
+ snd_printk(KERN_ERR SFX
+ "%s: Invalid fixup type %d\n",
+ codec->chip_name, fix->type);
+ break;
+ }
+ if (!fix->chained)
+ break;
+ if (++depth > 10)
+ break;
+ id = fix->chain_id;
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);
+
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct snd_pci_quirk *q;
+ int id = -1;
+ const char *name = NULL;
+
+ /* when model=nofixup is given, don't pick up any fixups */
+ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+ spec->fixup_list = NULL;
+ spec->fixup_id = -1;
+ return;
+ }
+
+ if (codec->modelname && models) {
+ while (models->name) {
+ if (!strcmp(codec->modelname, models->name)) {
+ id = models->id;
+ name = models->name;
+ break;
+ }
+ models++;
+ }
+ }
+ if (id < 0) {
+ q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+ if (q) {
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ }
+ }
+ if (id < 0) {
+ for (q = quirk; q->subvendor; q++) {
+ unsigned int vendorid =
+ q->subdevice | (q->subvendor << 16);
+ if (vendorid == codec->subsystem_id) {
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ break;
+ }
+ }
+ }
+
+ spec->fixup_id = id;
+ if (id >= 0) {
+ spec->fixup_list = fixlist;
+ spec->fixup_name = name;
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644
index 00000000000..2a7889dfbd1
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -0,0 +1,160 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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 __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+ AUTO_PIN_MIC,
+ AUTO_PIN_LINE_IN,
+ AUTO_PIN_CD,
+ AUTO_PIN_AUX,
+ AUTO_PIN_LAST
+};
+
+enum {
+ AUTO_PIN_LINE_OUT,
+ AUTO_PIN_SPEAKER_OUT,
+ AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS 8
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+ int line_outs;
+ /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+ int speaker_outs;
+ hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+ int hp_outs;
+ int line_out_type; /* AUTO_PIN_XXX_OUT */
+ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+ int dig_outs;
+ hda_nid_t dig_out_pins[2];
+ hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
+ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+ int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+/*
+ */
+
+struct hda_gen_spec {
+ /* fix-up list */
+ int fixup_id;
+ const struct hda_fixup *fixup_list;
+ const char *fixup_name;
+
+ /* additional init verbs */
+ struct snd_array verbs;
+};
+
+
+/*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+ hda_nid_t nid;
+ u32 val;
+};
+
+struct hda_model_fixup {
+ const int id;
+ const char *name;
+};
+
+struct hda_fixup {
+ int type;
+ bool chained;
+ int chain_id;
+ union {
+ const struct hda_pintbl *pins;
+ const struct hda_verb *verbs;
+ void (*func)(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+ } v;
+};
+
+/* fixup types */
+enum {
+ HDA_FIXUP_INVALID,
+ HDA_FIXUP_PINS,
+ HDA_FIXUP_VERBS,
+ HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+ HDA_FIXUP_ACT_PRE_PROBE,
+ HDA_FIXUP_ACT_PROBE,
+ HDA_FIXUP_ACT_INIT,
+ HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+ const struct hda_verb *list);
+void snd_hda_gen_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist);
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 841475cc13b..eb09a334832 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -334,78 +334,67 @@ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
return NULL;
}
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ hda_nid_t list[HDA_MAX_CONNECTIONS];
+ int len;
+
+ len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (len < 0)
+ return len;
+ return snd_hda_override_conn_list(codec, nid, len, list);
+}
+
/**
- * snd_hda_get_conn_list - get connection list
+ * snd_hda_get_connections - copy connection list
* @codec: the HDA codec
* @nid: NID to parse
- * @listp: the pointer to store NID list
+ * @conn_list: connection list array; when NULL, checks only the size
+ * @max_conns: max. number of connections to store
*
* Parses the connection list of the given widget and stores the list
* of NIDs.
*
* Returns the number of connections, or a negative error code.
*/
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
- const hda_nid_t **listp)
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
{
struct snd_array *array = &codec->conn_lists;
- int len, err;
- hda_nid_t list[HDA_MAX_CONNECTIONS];
+ int len;
hda_nid_t *p;
bool added = false;
again:
+ mutex_lock(&codec->hash_mutex);
+ len = -1;
/* if the connection-list is already cached, read it */
p = lookup_conn_list(array, nid);
if (p) {
- if (listp)
- *listp = p + 2;
- return p[1];
+ len = p[1];
+ if (conn_list && len > max_conns) {
+ snd_printk(KERN_ERR "hda_codec: "
+ "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ mutex_unlock(&codec->hash_mutex);
+ return -EINVAL;
+ }
+ if (conn_list && len)
+ memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
}
+ mutex_unlock(&codec->hash_mutex);
+ if (len >= 0)
+ return len;
if (snd_BUG_ON(added))
return -EINVAL;
- /* read the connection and add to the cache */
- len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
+ len = read_and_add_raw_conns(codec, nid);
if (len < 0)
return len;
- err = snd_hda_override_conn_list(codec, nid, len, list);
- if (err < 0)
- return err;
added = true;
goto again;
}
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
-
-/**
- * snd_hda_get_connections - copy connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
- *
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns)
-{
- const hda_nid_t *list;
- int len = snd_hda_get_conn_list(codec, nid, &list);
-
- if (len <= 0)
- return len;
- if (len > max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- len, nid);
- return -EINVAL;
- }
- memcpy(conn_list, list, len * sizeof(hda_nid_t));
- return len;
-}
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
/**
@@ -543,6 +532,7 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
hda_nid_t *p;
int i, old_used;
+ mutex_lock(&codec->hash_mutex);
p = lookup_conn_list(array, nid);
if (p)
*p = -1; /* invalidate the old entry */
@@ -553,10 +543,12 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
for (i = 0; i < len; i++)
if (!add_conn_list(array, list[i]))
goto error_add;
+ mutex_unlock(&codec->hash_mutex);
return 0;
error_add:
array->used = old_used;
+ mutex_unlock(&codec->hash_mutex);
return -ENOMEM;
}
EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -1255,6 +1247,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
+ mutex_init(&codec->hash_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1264,15 +1257,9 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
- if (codec->bus->modelname) {
- codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- snd_hda_codec_free(codec);
- return -ENODEV;
- }
- }
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spin_lock_init(&codec->power_lock);
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
* the caller has to power down appropriatley after initialization
@@ -1281,6 +1268,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
hda_keep_power_on(codec);
#endif
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname) {
+ snd_hda_codec_free(codec);
+ return -ENODEV;
+ }
+ }
+
list_add_tail(&codec->list, &bus->codec_list);
bus->caddr_tbl[codec_addr] = codec;
@@ -1603,6 +1598,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
}
+/* overwrite the value with the key in the caps hash */
+static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
+{
+ struct hda_amp_info *info;
+
+ mutex_lock(&codec->hash_mutex);
+ info = get_alloc_amp_hash(codec, key);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
+ return -EINVAL;
+ }
+ info->amp_caps = val;
+ info->head.val |= INFO_AMP_CAPS;
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+}
+
+/* query the value from the caps hash; if not found, fetch the current
+ * value from the given function and store in the hash
+ */
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
+ unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
+{
+ struct hda_amp_info *info;
+ unsigned int val;
+
+ mutex_lock(&codec->hash_mutex);
+ info = get_alloc_amp_hash(codec, key);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+ }
+ if (!(info->head.val & INFO_AMP_CAPS)) {
+ mutex_unlock(&codec->hash_mutex); /* for reentrance */
+ val = func(codec, nid, dir);
+ write_caps_hash(codec, key, val);
+ } else {
+ val = info->amp_caps;
+ mutex_unlock(&codec->hash_mutex);
+ }
+ return val;
+}
+
+static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
+ int direction)
+{
+ if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+ nid = codec->afg;
+ return snd_hda_param_read(codec, nid,
+ direction == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
@@ -1617,22 +1666,9 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
- if (!info)
- return 0;
- if (!(info->head.val & INFO_AMP_CAPS)) {
- if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
- nid = codec->afg;
- info->amp_caps = snd_hda_param_read(codec, nid,
- direction == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP :
- AC_PAR_AMP_IN_CAP);
- if (info->amp_caps)
- info->head.val |= INFO_AMP_CAPS;
- }
- return info->amp_caps;
+ return query_caps_hash(codec, nid, direction,
+ HDA_HASH_KEY(nid, direction, 0),
+ read_amp_cap);
}
EXPORT_SYMBOL_HDA(query_amp_caps);
@@ -1652,34 +1688,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
- if (!info)
- return -EINVAL;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
+ return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
}
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
- unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, key);
- if (!info)
- return 0;
- if (!info->head.val) {
- info->head.val |= INFO_AMP_CAPS;
- info->amp_caps = func(codec, nid);
- }
- return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
@@ -1697,7 +1711,7 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
*/
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
read_pin_cap);
}
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
@@ -1715,41 +1729,47 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps)
{
- struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
- if (!info)
- return -ENOMEM;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
+ return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
}
EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
+/* read or sync the hash value with the current value;
+ * call within hash_mutex
*/
-static unsigned int get_vol_mute(struct hda_codec *codec,
- struct hda_amp_info *info, hda_nid_t nid,
- int ch, int direction, int index)
+static struct hda_amp_info *
+update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int index)
{
- u32 val, parm;
-
- if (info->head.val & INFO_AMP_VOL(ch))
- return info->vol[ch];
+ struct hda_amp_info *info;
+ unsigned int parm, val = 0;
+ bool val_read = false;
- parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
- parm |= index;
- val = snd_hda_codec_read(codec, nid, 0,
+ retry:
+ info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+ if (!info)
+ return NULL;
+ if (!(info->head.val & INFO_AMP_VOL(ch))) {
+ if (!val_read) {
+ mutex_unlock(&codec->hash_mutex);
+ parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+ parm |= direction == HDA_OUTPUT ?
+ AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+ parm |= index;
+ val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val & 0xff;
- info->head.val |= INFO_AMP_VOL(ch);
- return info->vol[ch];
+ val &= 0xff;
+ val_read = true;
+ mutex_lock(&codec->hash_mutex);
+ goto retry;
+ }
+ info->vol[ch] = val;
+ info->head.val |= INFO_AMP_VOL(ch);
+ }
+ return info;
}
/*
- * write the current volume in info to the h/w and update the cache
+ * write the current volume in info to the h/w
*/
static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
hda_nid_t nid, int ch, int direction, int index,
@@ -1766,7 +1786,6 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
else
parm |= val;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val;
}
/**
@@ -1783,10 +1802,14 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index)
{
struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
- if (!info)
- return 0;
- return get_vol_mute(codec, info, nid, ch, direction, index);
+ unsigned int val = 0;
+
+ mutex_lock(&codec->hash_mutex);
+ info = update_amp_hash(codec, nid, ch, direction, index);
+ if (info)
+ val = info->vol[ch];
+ mutex_unlock(&codec->hash_mutex);
+ return val;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
@@ -1808,15 +1831,23 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
{
struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
- if (!info)
- return 0;
if (snd_BUG_ON(mask & ~0xff))
mask &= 0xff;
val &= mask;
- val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
- if (info->vol[ch] == val)
+
+ mutex_lock(&codec->hash_mutex);
+ info = update_amp_hash(codec, nid, ch, direction, idx);
+ if (!info) {
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
+ }
+ val |= info->vol[ch] & ~mask;
+ if (info->vol[ch] == val) {
+ mutex_unlock(&codec->hash_mutex);
return 0;
+ }
+ info->vol[ch] = val;
+ mutex_unlock(&codec->hash_mutex);
put_vol_mute(codec, info, nid, ch, direction, idx, val);
return 1;
}
@@ -2263,7 +2294,10 @@ int snd_hda_codec_reset(struct hda_codec *codec)
/* OK, let it free */
#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
+ cancel_delayed_work_sync(&codec->power_work);
+ codec->power_on = 0;
+ codec->power_transition = 0;
+ codec->power_jiffies = jiffies;
flush_workqueue(codec->bus->workq);
#endif
snd_hda_ctls_clear(codec);
@@ -2859,12 +2893,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
ucontrol->value.iec958.status[0] = spdif->status & 0xff;
ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
@@ -2950,12 +2987,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
- hda_nid_t nid = spdif->nid;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
spdif->status = ucontrol->value.iec958.status[0] |
((unsigned int)ucontrol->value.iec958.status[1] << 8) |
((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -2977,9 +3016,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+ mutex_unlock(&codec->spdif_mutex);
return 0;
}
@@ -2999,12 +3041,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
- hda_nid_t nid = spdif->nid;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
unsigned short val;
int change;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
val = spdif->ctls & ~AC_DIG1_ENABLE;
if (ucontrol->value.integer.value[0])
val |= AC_DIG1_ENABLE;
@@ -3092,6 +3136,9 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
+/* get the hda_spdif_out entry from the given NID
+ * call within spdif_mutex lock
+ */
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
hda_nid_t nid)
{
@@ -3108,9 +3155,10 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
{
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
spdif->nid = (u16)-1;
mutex_unlock(&codec->spdif_mutex);
}
@@ -3118,10 +3166,11 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign);
void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
{
- struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+ struct hda_spdif_out *spdif;
unsigned short val;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
if (spdif->nid != nid) {
spdif->nid = nid;
val = spdif->ctls;
@@ -3486,11 +3535,14 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D3);
#ifdef CONFIG_SND_HDA_POWER_SAVE
- snd_hda_update_power_acct(codec);
cancel_delayed_work(&codec->power_work);
+ spin_lock(&codec->power_lock);
+ snd_hda_update_power_acct(codec);
+ trace_hda_power_down(codec);
codec->power_on = 0;
codec->power_transition = 0;
codec->power_jiffies = jiffies;
+ spin_unlock(&codec->power_lock);
#endif
}
@@ -3499,6 +3551,10 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
+ /* set as if powered on for avoiding re-entering the resume
+ * in the resume / power-save sequence
+ */
+ hda_keep_power_on(codec);
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
@@ -3514,6 +3570,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
}
+ snd_hda_power_down(codec); /* flag down before returning */
}
#endif /* CONFIG_PM */
@@ -3665,7 +3722,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
}
EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
unsigned int val = 0;
if (nid != codec->afg &&
@@ -3680,11 +3738,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
get_pcm_param);
}
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
+ int dir)
{
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (!streams || streams == -1)
@@ -3696,7 +3755,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
- return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+ return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
get_stream_param);
}
@@ -3775,11 +3834,13 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
bps = 20;
}
}
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
if (streams & AC_SUPFMT_FLOAT32) {
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
if (!bps)
bps = 32;
}
+#endif
if (streams == AC_SUPFMT_AC3) {
/* should be exclusive */
/* temporary hack: we have still no proper support
@@ -4283,12 +4344,18 @@ static void hda_power_work(struct work_struct *work)
container_of(work, struct hda_codec, power_work.work);
struct hda_bus *bus = codec->bus;
+ spin_lock(&codec->power_lock);
+ if (codec->power_transition > 0) { /* during power-up sequence? */
+ spin_unlock(&codec->power_lock);
+ return;
+ }
if (!codec->power_on || codec->power_count) {
codec->power_transition = 0;
+ spin_unlock(&codec->power_lock);
return;
}
+ spin_unlock(&codec->power_lock);
- trace_hda_power_down(codec);
hda_call_codec_suspend(codec);
if (bus->ops.pm_notify)
bus->ops.pm_notify(bus);
@@ -4296,9 +4363,11 @@ static void hda_power_work(struct work_struct *work)
static void hda_keep_power_on(struct hda_codec *codec)
{
+ spin_lock(&codec->power_lock);
codec->power_count++;
codec->power_on = 1;
codec->power_jiffies = jiffies;
+ spin_unlock(&codec->power_lock);
}
/* update the power on/off account with the current jiffies */
@@ -4323,19 +4392,31 @@ void snd_hda_power_up(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
+ spin_lock(&codec->power_lock);
codec->power_count++;
- if (codec->power_on || codec->power_transition)
+ if (codec->power_on || codec->power_transition > 0) {
+ spin_unlock(&codec->power_lock);
return;
+ }
+ spin_unlock(&codec->power_lock);
+ cancel_delayed_work_sync(&codec->power_work);
+
+ spin_lock(&codec->power_lock);
trace_hda_power_up(codec);
snd_hda_update_power_acct(codec);
codec->power_on = 1;
codec->power_jiffies = jiffies;
+ codec->power_transition = 1; /* avoid reentrance */
+ spin_unlock(&codec->power_lock);
+
if (bus->ops.pm_notify)
bus->ops.pm_notify(bus);
hda_call_codec_resume(codec);
- cancel_delayed_work(&codec->power_work);
+
+ spin_lock(&codec->power_lock);
codec->power_transition = 0;
+ spin_unlock(&codec->power_lock);
}
EXPORT_SYMBOL_HDA(snd_hda_power_up);
@@ -4351,14 +4432,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
*/
void snd_hda_power_down(struct hda_codec *codec)
{
+ spin_lock(&codec->power_lock);
--codec->power_count;
- if (!codec->power_on || codec->power_count || codec->power_transition)
+ if (!codec->power_on || codec->power_count || codec->power_transition) {
+ spin_unlock(&codec->power_lock);
return;
+ }
if (power_save(codec)) {
- codec->power_transition = 1; /* avoid reentrance */
+ codec->power_transition = -1; /* avoid reentrance */
queue_delayed_work(codec->bus->workq, &codec->power_work,
msecs_to_jiffies(power_save(codec) * 1000));
}
+ spin_unlock(&codec->power_lock);
}
EXPORT_SYMBOL_HDA(snd_hda_power_down);
@@ -4710,11 +4795,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
{
const hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
- struct hda_spdif_out *spdif =
- snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+ struct hda_spdif_out *spdif;
int i;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
if (mout->dig_out_nid && mout->share_spdif &&
mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
if (chs == 2 &&
@@ -4795,601 +4880,58 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
-{
- for (; *list; list++)
- if (*list == nid)
- return 1;
- return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
- int num_pins)
-{
- int i, j;
- short seq;
- hda_nid_t nid;
-
- for (i = 0; i < num_pins; i++) {
- for (j = i + 1; j < num_pins; j++) {
- if (sequences[i] > sequences[j]) {
- seq = sequences[i];
- sequences[i] = sequences[j];
- sequences[j] = seq;
- nid = pins[i];
- pins[i] = pins[j];
- pins[j] = nid;
- }
- }
- }
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
- int type)
-{
- if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
- cfg->inputs[cfg->num_inputs].pin = nid;
- cfg->inputs[cfg->num_inputs].type = type;
- cfg->num_inputs++;
- }
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
- int i, j;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- for (j = i + 1; j < cfg->num_inputs; j++) {
- if (cfg->inputs[i].type > cfg->inputs[j].type) {
- struct auto_pin_cfg_item tmp;
- tmp = cfg->inputs[i];
- cfg->inputs[i] = cfg->inputs[j];
- cfg->inputs[j] = tmp;
- }
- }
- }
-}
-
-/* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- * 4-ch: front/surr => OK as it is
- * 6-ch: front/clfe/surr
- * 8-ch: front/clfe/rear/side|fc
- */
-static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
-{
- hda_nid_t nid;
-
- switch (nums) {
- case 3:
- case 4:
- nid = pins[1];
- pins[1] = pins[2];
- pins[2] = nid;
- break;
- }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0]. So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- const hda_nid_t *ignore_nids,
- unsigned int cond_flags)
-{
- hda_nid_t nid, end_nid;
- short seq, assoc_line_out;
- short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
- short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
- short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
- int i;
-
- memset(cfg, 0, sizeof(*cfg));
-
- memset(sequences_line_out, 0, sizeof(sequences_line_out));
- memset(sequences_speaker, 0, sizeof(sequences_speaker));
- memset(sequences_hp, 0, sizeof(sequences_hp));
- assoc_line_out = 0;
-
- codec->ignore_misc_bit = true;
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wid_caps);
- unsigned int def_conf;
- short assoc, loc, conn, dev;
-
- /* read all default configuration for pin complex */
- if (wid_type != AC_WID_PIN)
- continue;
- /* ignore the given nids (e.g. pc-beep returns error) */
- if (ignore_nids && is_in_nid_list(nid, ignore_nids))
- continue;
-
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
- AC_DEFCFG_MISC_NO_PRESENCE))
- codec->ignore_misc_bit = false;
- conn = get_defcfg_connect(def_conf);
- if (conn == AC_JACK_PORT_NONE)
- continue;
- loc = get_defcfg_location(def_conf);
- dev = get_defcfg_device(def_conf);
-
- /* workaround for buggy BIOS setups */
- if (dev == AC_JACK_LINE_OUT) {
- if (conn == AC_JACK_PORT_FIXED)
- dev = AC_JACK_SPEAKER;
- }
-
- switch (dev) {
- case AC_JACK_LINE_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
-
- if (!(wid_caps & AC_WCAP_STEREO))
- if (!cfg->mono_out_pin)
- cfg->mono_out_pin = nid;
- if (!assoc)
- continue;
- if (!assoc_line_out)
- assoc_line_out = assoc;
- else if (assoc_line_out != assoc)
- continue;
- if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
- continue;
- cfg->line_out_pins[cfg->line_outs] = nid;
- sequences_line_out[cfg->line_outs] = seq;
- cfg->line_outs++;
- break;
- case AC_JACK_SPEAKER:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
- continue;
- cfg->speaker_pins[cfg->speaker_outs] = nid;
- sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
- cfg->speaker_outs++;
- break;
- case AC_JACK_HP_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
- continue;
- cfg->hp_pins[cfg->hp_outs] = nid;
- sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
- cfg->hp_outs++;
- break;
- case AC_JACK_MIC_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
- break;
- case AC_JACK_LINE_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
- break;
- case AC_JACK_CD:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
- break;
- case AC_JACK_AUX:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
- break;
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
- continue;
- cfg->dig_out_pins[cfg->dig_outs] = nid;
- cfg->dig_out_type[cfg->dig_outs] =
- (loc == AC_JACK_LOC_HDMI) ?
- HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
- cfg->dig_outs++;
- break;
- case AC_JACK_SPDIF_IN:
- case AC_JACK_DIG_OTHER_IN:
- cfg->dig_in_pin = nid;
- if (loc == AC_JACK_LOC_HDMI)
- cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
- else
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
- break;
- }
- }
-
- /* FIX-UP:
- * If no line-out is defined but multiple HPs are found,
- * some of them might be the real line-outs.
- */
- if (!cfg->line_outs && cfg->hp_outs > 1 &&
- !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
- int i = 0;
- while (i < cfg->hp_outs) {
- /* The real HPs should have the sequence 0x0f */
- if ((sequences_hp[i] & 0x0f) == 0x0f) {
- i++;
- continue;
- }
- /* Move it to the line-out table */
- cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
- sequences_line_out[cfg->line_outs] = sequences_hp[i];
- cfg->line_outs++;
- cfg->hp_outs--;
- memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
- sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
- memmove(sequences_hp + i, sequences_hp + i + 1,
- sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
- }
- memset(cfg->hp_pins + cfg->hp_outs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
- if (!cfg->hp_outs)
- cfg->line_out_type = AUTO_PIN_HP_OUT;
-
- }
-
- /* sort by sequence */
- sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
- cfg->line_outs);
- sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
- cfg->speaker_outs);
- sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
- cfg->hp_outs);
-
- /*
- * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
- * as a primary output
- */
- if (!cfg->line_outs &&
- !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
- if (cfg->speaker_outs) {
- cfg->line_outs = cfg->speaker_outs;
- memcpy(cfg->line_out_pins, cfg->speaker_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = 0;
- memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- } else if (cfg->hp_outs) {
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- }
- }
-
- reorder_outputs(cfg->line_outs, cfg->line_out_pins);
- reorder_outputs(cfg->hp_outs, cfg->hp_pins);
- reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
-
- sort_autocfg_input_pins(cfg);
-
- /*
- * debug prints of the parsed results
- */
- snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
- cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
- cfg->line_out_pins[2], cfg->line_out_pins[3],
- cfg->line_out_pins[4],
- cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
- (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
- "speaker" : "line"));
- snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->speaker_outs, cfg->speaker_pins[0],
- cfg->speaker_pins[1], cfg->speaker_pins[2],
- cfg->speaker_pins[3], cfg->speaker_pins[4]);
- snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->hp_outs, cfg->hp_pins[0],
- cfg->hp_pins[1], cfg->hp_pins[2],
- cfg->hp_pins[3], cfg->hp_pins[4]);
- snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
- if (cfg->dig_outs)
- snd_printd(" dig-out=0x%x/0x%x\n",
- cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
- for (i = 0; i < cfg->num_inputs; i++) {
- snd_printd(" %s=0x%x",
- hda_get_autocfg_input_label(codec, cfg, i),
- cfg->inputs[i].pin);
- }
- snd_printd("\n");
- if (cfg->dig_in_pin)
- snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
- unsigned int loc = get_defcfg_location(def_conf);
- unsigned int conn = get_defcfg_connect(def_conf);
- if (conn == AC_JACK_PORT_NONE)
- return INPUT_PIN_ATTR_UNUSED;
- /* Windows may claim the internal mic to be BOTH, too */
- if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
- return INPUT_PIN_ATTR_DOCK;
- if (loc == AC_JACK_LOC_REAR)
- return INPUT_PIN_ATTR_REAR;
- if (loc == AC_JACK_LOC_FRONT)
- return INPUT_PIN_ATTR_FRONT;
- return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
-
-/**
- * hda_get_input_pin_label - Give a label for the given input pin
- *
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
- */
-
-static const char *hda_get_input_pin_label(struct hda_codec *codec,
- hda_nid_t pin, bool check_location)
-{
- unsigned int def_conf;
- static const char * const mic_names[] = {
- "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
- };
- int attr;
-
- def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_MIC_IN:
- if (!check_location)
- return "Mic";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- return mic_names[attr - 1];
- case AC_JACK_LINE_IN:
- if (!check_location)
- return "Line";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- if (attr == INPUT_PIN_ATTR_DOCK)
- return "Dock Line";
- return "Line";
- case AC_JACK_AUX:
- return "Aux";
- case AC_JACK_CD:
- return "CD";
- case AC_JACK_SPDIF_IN:
- return "SPDIF In";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital In";
- default:
- return "Misc";
- }
-}
-
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label. In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- unsigned int defc;
- int i, attr, attr2;
-
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
- attr = snd_hda_get_input_pin_attr(defc);
- /* for internal or docking mics, we need locations */
- if (attr <= INPUT_PIN_ATTR_NORMAL)
- return 1;
-
- attr = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
- attr2 = snd_hda_get_input_pin_attr(defc);
- if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
- if (attr && attr != attr2)
- return 1; /* different locations found */
- attr = attr2;
- }
- }
- return 0;
-}
-
/**
- * hda_get_autocfg_input_label - Get a label for the given input
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
*
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- int type = cfg->inputs[input].type;
- int has_multiple_pins = 0;
-
- if ((input > 0 && cfg->inputs[input - 1].type == type) ||
- (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
- has_multiple_pins = 1;
- if (has_multiple_pins && type == AUTO_PIN_MIC)
- has_multiple_pins &= check_mic_location_need(codec, cfg, input);
- return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
- has_multiple_pins);
-}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
- int i;
- for (i = 0; i < nums; i++)
- if (list[i] == nid)
- return i;
- return -1;
-}
-
-/* get a unique suffix or an index number */
-static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
- int num_pins, int *indexp)
-{
- static const char * const channel_sfx[] = {
- " Front", " Surround", " CLFE", " Side"
- };
- int i;
-
- i = find_idx_in_nid_list(nid, pins, num_pins);
- if (i < 0)
- return NULL;
- if (num_pins == 1)
- return "";
- if (num_pins > ARRAY_SIZE(channel_sfx)) {
- if (indexp)
- *indexp = i;
- return "";
- }
- return channel_sfx[i];
-}
-
-static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
- const struct auto_pin_cfg *cfg,
- const char *name, char *label, int maxlen,
- int *indexp)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int attr = snd_hda_get_input_pin_attr(def_conf);
- const char *pfx = "", *sfx = "";
-
- /* handle as a speaker if it's a fixed line-out */
- if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
- name = "Speaker";
- /* check the location */
- switch (attr) {
- case INPUT_PIN_ATTR_DOCK:
- pfx = "Dock ";
- break;
- case INPUT_PIN_ATTR_FRONT:
- pfx = "Front ";
- break;
- }
- if (cfg) {
- /* try to give a unique suffix if needed */
- sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
- indexp);
- if (!sfx)
- sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
- indexp);
- if (!sfx) {
- /* don't add channel suffix for Headphone controls */
- int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
- cfg->hp_outs);
- if (idx >= 0)
- *indexp = idx;
- sfx = "";
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+ unsigned int oldval;
+ oldval = snd_hda_codec_read(codec, pin, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* Exception: if the default pin setup is vref50, we give it priority */
+ if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+ return AC_PINCTL_VREF_80;
+ else if (pincap & AC_PINCAP_VREF_50)
+ return AC_PINCTL_VREF_50;
+ else if (pincap & AC_PINCAP_VREF_100)
+ return AC_PINCTL_VREF_100;
+ else if (pincap & AC_PINCAP_VREF_GRD)
+ return AC_PINCTL_VREF_GRD;
+ return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
+
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached)
+{
+ if (val) {
+ unsigned int cap = snd_hda_query_pin_caps(codec, pin);
+ if (cap && (val & AC_PINCTL_OUT_EN)) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) &&
+ !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
}
- }
- snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
- return 1;
-}
-
-/**
- * snd_hda_get_pin_label - Get a label for the given I/O pin
- *
- * Get a label for the given pin. This function works for both input and
- * output pins. When @cfg is given as non-NULL, the function tries to get
- * an optimized label using hda_get_autocfg_input_label().
- *
- * This function tries to give a unique label string for the pin as much as
- * possible. For example, when the multiple line-outs are present, it adds
- * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
- * If no unique name with a suffix is available and @indexp is non-NULL, the
- * index number is stored in the pointer.
- */
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
- const struct auto_pin_cfg *cfg,
- char *label, int maxlen, int *indexp)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- const char *name = NULL;
- int i;
-
- if (indexp)
- *indexp = 0;
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- return 0;
-
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_LINE_OUT:
- return fill_audio_out_name(codec, nid, cfg, "Line Out",
- label, maxlen, indexp);
- case AC_JACK_SPEAKER:
- return fill_audio_out_name(codec, nid, cfg, "Speaker",
- label, maxlen, indexp);
- case AC_JACK_HP_OUT:
- return fill_audio_out_name(codec, nid, cfg, "Headphone",
- label, maxlen, indexp);
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
- name = "HDMI";
- else
- name = "SPDIF";
- if (cfg && indexp) {
- i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
- cfg->dig_outs);
- if (i >= 0)
- *indexp = i;
- }
- break;
- default:
- if (cfg) {
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].pin != nid)
- continue;
- name = hda_get_autocfg_input_label(codec, cfg, i);
- if (name)
- break;
- }
+ if (cap && (val & AC_PINCTL_IN_EN)) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
}
- if (!name)
- name = hda_get_input_pin_label(codec, nid, true);
- break;
}
- if (!name)
- return 0;
- strlcpy(label, name, maxlen);
- return 1;
+ if (cached)
+ return snd_hda_codec_update_cache(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ else
+ return snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
-EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl);
/**
* snd_hda_add_imux_item - Add an item to input_mux
@@ -5444,8 +4986,6 @@ int snd_hda_suspend(struct hda_bus *bus)
list_for_each_entry(codec, &bus->codec_list, list) {
if (hda_codec_is_power_on(codec))
hda_call_codec_suspend(codec);
- if (codec->patch_ops.post_suspend)
- codec->patch_ops.post_suspend(codec);
}
return 0;
}
@@ -5465,10 +5005,7 @@ int snd_hda_resume(struct hda_bus *bus)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
- if (codec->patch_ops.pre_resume)
- codec->patch_ops.pre_resume(codec);
- if (snd_hda_codec_needs_resume(codec))
- hda_call_codec_resume(codec);
+ hda_call_codec_resume(codec);
}
return 0;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 56b4f74c0b1..54b52819fb4 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -704,8 +704,6 @@ struct hda_codec_ops {
unsigned int power_state);
#ifdef CONFIG_PM
int (*suspend)(struct hda_codec *codec, pm_message_t state);
- int (*post_suspend)(struct hda_codec *codec);
- int (*pre_resume)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -829,6 +827,7 @@ struct hda_codec {
struct mutex spdif_mutex;
struct mutex control_mutex;
+ struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -861,12 +860,13 @@ struct hda_codec {
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
#ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */
- unsigned int power_transition :1; /* power-state in transition */
+ int power_transition; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
+ spinlock_t power_lock;
#endif
/* codec-specific additional proc output */
@@ -911,10 +911,13 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *start_id);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
+static inline int
+snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_get_connections(codec, nid, NULL, 0);
+}
int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
- const hda_nid_t **listp);
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
@@ -1051,12 +1054,10 @@ const char *snd_hda_get_jack_location(u32 cfg);
#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_power_up(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_power_up(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
#endif
#ifdef CONFIG_SND_HDA_PATCH_LOADER
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1f350522bed..4ab8102f87e 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -497,6 +497,7 @@ enum {
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
AZX_DRIVER_CTX,
+ AZX_DRIVER_CTHDA,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -518,6 +519,7 @@ enum {
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */
#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
@@ -533,6 +535,9 @@ enum {
(AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
AZX_DCAPS_ALIGN_BUFSIZE)
+#define AZX_DCAPS_PRESET_CTHDA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
+
static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -546,6 +551,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
[AZX_DRIVER_CTX] = "HDA Creative",
+ [AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@@ -1285,7 +1291,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/*
* set up a BDL entry
*/
-static int setup_bdle(struct snd_pcm_substream *substream,
+static int setup_bdle(struct azx *chip,
+ struct snd_pcm_substream *substream,
struct azx_dev *azx_dev, u32 **bdlp,
int ofs, int size, int with_ioc)
{
@@ -1304,6 +1311,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
bdl[1] = cpu_to_le32(upper_32_bits(addr));
/* program the size field of the BDL entry */
chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ /* one BDLE cannot cross 4K boundary on CTHDA chips */
+ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
+ u32 remain = 0x1000 - (ofs & 0xfff);
+ if (chunk > remain)
+ chunk = remain;
+ }
bdl[2] = cpu_to_le32(chunk);
/* program the IOC to enable interrupt
* only when the whole fragment is processed
@@ -1356,7 +1369,7 @@ static int azx_setup_periods(struct azx *chip,
bdl_pos_adj[chip->dev_index]);
pos_adj = 0;
} else {
- ofs = setup_bdle(substream, azx_dev,
+ ofs = setup_bdle(chip, substream, azx_dev,
&bdl, ofs, pos_adj,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
@@ -1366,10 +1379,10 @@ static int azx_setup_periods(struct azx *chip,
pos_adj = 0;
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
period_bytes - pos_adj, 0);
else
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
period_bytes,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
@@ -2353,17 +2366,6 @@ static void azx_power_notify(struct hda_bus *bus)
* power management
*/
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- return 1;
- }
- return 0;
-}
-
static int azx_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
@@ -2410,8 +2412,7 @@ static int azx_resume(struct pci_dev *pci)
return -EIO;
azx_init_pci(chip);
- if (snd_hda_codecs_inuse(chip->bus))
- azx_init_chip(chip, 1);
+ azx_init_chip(chip, 1);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -2565,6 +2566,8 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
/* forced codec slots */
SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+ /* WinFast VP200 H (Teradici) user reported broken communication */
+ SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
{}
};
@@ -3130,6 +3133,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
#endif
+ /* CTHDA chips */
+ { PCI_DEVICE(0x1102, 0x0010),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+ { PCI_DEVICE(0x1102, 0x0012),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
/* Vortex86MX */
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
/* VMware HDAudio */
@@ -3148,7 +3156,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
MODULE_DEVICE_TABLE(pci, azx_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver azx_driver = {
.name = KBUILD_MODNAME,
.id_table = azx_ids,
.probe = azx_probe,
@@ -3159,15 +3167,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_azx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
+module_pci_driver(azx_driver);
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index d68948499fb..2dd1c113a4c 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -17,6 +17,7 @@
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_jack.h"
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index c66655cf413..8ae52465ec5 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -12,6 +12,8 @@
#ifndef __SOUND_HDA_JACK_H
#define __SOUND_HDA_JACK_H
+struct auto_pin_cfg;
+
struct hda_jack_tbl {
hda_nid_t nid;
unsigned char action; /* event action (0 = none) */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 0ec9248165b..9a096a8e0fc 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -262,6 +262,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_index_ret);
/*
* Channel mode helper
@@ -393,72 +395,7 @@ struct hda_bus_unsolicited {
struct hda_bus *bus;
};
-/*
- * Helper for automatic pin configuration
- */
-
-enum {
- AUTO_PIN_MIC,
- AUTO_PIN_LINE_IN,
- AUTO_PIN_CD,
- AUTO_PIN_AUX,
- AUTO_PIN_LAST
-};
-
-enum {
- AUTO_PIN_LINE_OUT,
- AUTO_PIN_SPEAKER_OUT,
- AUTO_PIN_HP_OUT
-};
-
-#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
-#define AUTO_CFG_MAX_INS 8
-
-struct auto_pin_cfg_item {
- hda_nid_t pin;
- int type;
-};
-
-struct auto_pin_cfg;
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input);
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
- const struct auto_pin_cfg *cfg,
- char *label, int maxlen, int *indexp);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
- int index, int *type_index_ret);
-
-enum {
- INPUT_PIN_ATTR_UNUSED, /* pin not connected */
- INPUT_PIN_ATTR_INT, /* internal mic/line-in */
- INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
- INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
- INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
- INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
-};
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
- int line_outs;
- /* sorted in the order of Front/Surr/CLFE/Side */
- hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
- int speaker_outs;
- hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
- int hp_outs;
- int line_out_type; /* AUTO_PIN_XXX_OUT */
- hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- int num_inputs;
- struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
- int dig_outs;
- hda_nid_t dig_out_pins[2];
- hda_nid_t dig_in_pin;
- hda_nid_t mono_out_pin;
- int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
- int dig_in_type; /* HDA_PCM_TYPE_XXX */
-};
-
+/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
#define get_defcfg_association(cfg) \
@@ -472,19 +409,6 @@ struct auto_pin_cfg {
#define get_defcfg_misc(cfg) \
((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
-/* bit-flags for snd_hda_parse_pin_def_config() behavior */
-#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
-#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
-
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- const hda_nid_t *ignore_nids,
- unsigned int cond_flags);
-
-/* older function */
-#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
- snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
-
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
@@ -502,6 +426,46 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities. For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
/*
* get widget capabilities
*/
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 7143393927d..d8b2d6dee98 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -28,6 +28,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
@@ -1742,9 +1743,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
if (! ad198x_eapd_put(kcontrol, ucontrol))
return 0;
/* change speaker pin appropriately */
- snd_hda_codec_write(codec, 0x05, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_eapd ? PIN_OUT : 0);
+ snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
/* toggle HP mute appropriately */
snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
HDA_AMP_MUTE,
@@ -3103,7 +3102,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
int dac_idx)
{
/* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_set_pin_ctl(codec, nid, pin_type);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
switch (nid) {
case 0x11: /* port-A - DAC 03 */
@@ -3157,6 +3156,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
int type = cfg->inputs[i].type;
+ int val;
switch (nid) {
case 0x15: /* port-C */
snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3165,8 +3165,10 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
break;
}
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
+ val = PIN_IN;
+ if (type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, nid);
+ snd_hda_set_pin_ctl(codec, nid, val);
if (nid != AD1988_PIN_CD_NID)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 09ccfabb4a1..19ae14f739c 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -26,6 +26,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
/*
*/
@@ -341,8 +342,7 @@ static int ca0110_build_pcms(struct hda_codec *codec)
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -356,8 +356,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+ snd_hda_get_default_vref(codec, pin));
if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 21d91d580da..d0d3540e39e 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -30,6 +30,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#define WIDGET_CHIP_CTRL 0x15
#define WIDGET_DSP_CTRL 0x16
@@ -239,8 +240,7 @@ enum get_set {
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -254,9 +254,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_VREF80);
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+ snd_hda_get_default_vref(codec, pin));
if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index c83ccdba1e5..9647ed4d792 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -26,6 +26,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_jack.h"
#include <sound/tlv.h>
@@ -933,8 +934,7 @@ static void cs_automute(struct hda_codec *codec)
pin_ctl = 0;
nid = cfg->speaker_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
+ snd_hda_set_pin_ctl(codec, nid, pin_ctl);
}
if (spec->gpio_eapd_hp) {
unsigned int gpio = hp_present ?
@@ -948,16 +948,14 @@ static void cs_automute(struct hda_codec *codec)
/* mute HPs if spdif jack (SENSE_B) is present */
for (i = 0; i < cfg->hp_outs; i++) {
nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_set_pin_ctl(codec, nid,
(spdif_present && spec->sense_b) ? 0 : PIN_HP);
}
/* SPDIF TX on/off */
if (cfg->dig_outs) {
nid = cfg->dig_out_pins[0];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_set_pin_ctl(codec, nid,
spdif_present ? PIN_OUT : 0);
}
@@ -1024,13 +1022,11 @@ static void init_output(struct hda_codec *codec)
/* set appropriate pin controls */
for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT);
/* HP */
for (i = 0; i < cfg->hp_outs; i++) {
hda_nid_t nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ snd_hda_set_pin_ctl(codec, nid, PIN_HP);
if (!cfg->speaker_outs)
continue;
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
@@ -1041,8 +1037,7 @@ static void init_output(struct hda_codec *codec)
/* Speaker */
for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT);
/* SPDIF is enabled on presence detect for CS421x */
if (spec->hp_detect || spec->spdif_detect)
@@ -1063,14 +1058,9 @@ static void init_input(struct hda_codec *codec)
continue;
/* set appropriate pin control and mute first */
ctl = PIN_IN;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- unsigned int caps = snd_hda_query_pin_caps(codec, pin);
- caps >>= AC_PINCAP_VREF_SHIFT;
- if (caps & AC_PINCAP_VREF_80)
- ctl = PIN_VREF80;
- }
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ ctl |= snd_hda_get_default_vref(codec, pin);
+ snd_hda_set_pin_ctl(codec, pin, ctl);
snd_hda_codec_write(codec, spec->adc_nid[i], 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_MUTE(spec->adc_idx[i]));
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index b6767b4ced4..c8fdaaefe70 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -29,6 +29,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#define NUM_PINS 11
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index d906c5b74cf..3acb5824ad3 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -30,6 +30,7 @@
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
@@ -66,6 +67,7 @@ struct imux_info {
};
struct conexant_spec {
+ struct hda_gen_spec gen;
const struct snd_kcontrol_new *mixers[5];
int num_mixers;
@@ -141,6 +143,7 @@ struct conexant_spec {
unsigned int hp_laptop:1;
unsigned int asus:1;
unsigned int pin_eapd_ctrls:1;
+ unsigned int fixup_stereo_dmic:1;
unsigned int adc_switching:1;
@@ -1601,17 +1604,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
unsigned int pinctl;
/* headphone pin */
pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x16, pinctl);
/* speaker pin */
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1a, pinctl);
/* on ideapad there is an additional speaker (subwoofer) to mute */
if (spec->ideapad)
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1b, pinctl);
}
/* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1996,8 +1995,7 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
/* Port A (HP) */
pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x19, pinctl);
/* Port D (HP/LO) */
pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
@@ -2010,13 +2008,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
if (!hp_port_d_present(spec))
pinctl = 0;
}
- snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1c, pinctl);
/* CLASS_D AMP */
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
+ snd_hda_set_pin_ctl(codec, 0x1f, pinctl);
}
/* turn on/off EAPD (+ mute HP) as a master switch */
@@ -2047,8 +2043,7 @@ static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
/* Even though port F is the DC input, the bias is controlled on port B.
* we also leave that port as an active input (but unselected) in DC mode
* just in case that is necessary to make the bias setting take effect. */
- return snd_hda_codec_write_cache(codec, 0x1a, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
+ return snd_hda_set_pin_ctl_cache(codec, 0x1a,
cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
}
@@ -2081,14 +2076,14 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec)
}
/* disable DC (port F) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, 0x1e, 0);
/* external mic, port B */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_set_pin_ctl(codec, 0x1a,
spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
/* internal mic, port C */
- snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_set_pin_ctl(codec, 0x1b,
spec->ext_mic_present ? 0 : PIN_VREF80);
}
@@ -3357,9 +3352,7 @@ static void do_automute(struct hda_codec *codec, int num_pins,
struct conexant_spec *spec = codec->spec;
int i;
for (i = 0; i < num_pins; i++)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- on ? PIN_OUT : 0);
+ snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0);
if (spec->pin_eapd_ctrls)
cx_auto_turn_eapd(codec, num_pins, pins, on);
}
@@ -3976,8 +3969,7 @@ static void cx_auto_init_output(struct hda_codec *codec)
if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) &
AC_PINCAP_HP_DRV)
val |= AC_PINCTL_HP_EN;
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val);
}
mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
@@ -4030,13 +4022,11 @@ static void cx_auto_init_input(struct hda_codec *codec)
}
for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int type;
+ hda_nid_t pin = cfg->inputs[i].pin;
+ unsigned int type = PIN_IN;
if (cfg->inputs[i].type == AUTO_PIN_MIC)
- type = PIN_VREF80;
- else
- type = PIN_IN;
- snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+ type |= snd_hda_get_default_vref(codec, pin);
+ snd_hda_set_pin_ctl(codec, pin, type);
}
if (spec->auto_mic) {
@@ -4063,11 +4053,9 @@ static void cx_auto_init_digital(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
if (spec->multiout.dig_out_nid)
- snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT);
if (spec->dig_in_nid)
- snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+ snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN);
}
static int cx_auto_init(struct hda_codec *codec)
@@ -4084,9 +4072,9 @@ static int cx_auto_init(struct hda_codec *codec)
static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
const char *dir, int cidx,
- hda_nid_t nid, int hda_dir, int amp_idx)
+ hda_nid_t nid, int hda_dir, int amp_idx, int chs)
{
- static char name[32];
+ static char name[44];
static struct snd_kcontrol_new knew[] = {
HDA_CODEC_VOLUME(name, 0, 0, 0),
HDA_CODEC_MUTE(name, 0, 0, 0),
@@ -4096,7 +4084,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
for (i = 0; i < 2; i++) {
struct snd_kcontrol *kctl;
- knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
+ knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
hda_dir);
knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
knew[i].index = cidx;
@@ -4115,7 +4103,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
}
#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \
- cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
+ cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
#define cx_auto_add_pb_volume(codec, nid, str, idx) \
cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
@@ -4185,6 +4173,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec)
return 0;
}
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
+ be split in two independent channels.
+ dest_label must be at least 44 characters. */
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
+ char *dest_label, int nid)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->fixup_stereo_dmic)
+ return 0;
+
+ for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+ int def_conf;
+ if (spec->autocfg.inputs[i].pin != nid)
+ continue;
+
+ if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
+ return 0;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
+ return 0;
+
+ /* Finally found the inverted internal mic! */
+ snprintf(dest_label, 44, "Inverted %s", label);
+ return 1;
+ }
+ return 0;
+}
+
static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
const char *label, const char *pfx,
int cidx)
@@ -4193,14 +4211,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
int i;
for (i = 0; i < spec->num_adc_nids; i++) {
+ char rightch_label[44];
hda_nid_t adc_nid = spec->adc_nids[i];
int idx = get_input_connection(codec, adc_nid, nid);
if (idx < 0)
continue;
if (codec->single_adc_amp)
idx = 0;
+
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+ /* Make two independent kcontrols for left and right */
+ int err = cx_auto_add_volume_idx(codec, label, pfx,
+ cidx, adc_nid, HDA_INPUT, idx, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_add_volume_idx(codec, rightch_label, pfx,
+ cidx, adc_nid, HDA_INPUT, idx, 2);
+ }
return cx_auto_add_volume_idx(codec, label, pfx,
- cidx, adc_nid, HDA_INPUT, idx);
+ cidx, adc_nid, HDA_INPUT, idx, 3);
}
return 0;
}
@@ -4213,9 +4242,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
int i, con;
nid = spec->imux_info[idx].pin;
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ char rightch_label[44];
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+ int err = cx_auto_add_volume_idx(codec, label, " Boost",
+ cidx, nid, HDA_INPUT, 0, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
+ cidx, nid, HDA_INPUT, 0, 2);
+ }
return cx_auto_add_volume(codec, label, " Boost", cidx,
nid, HDA_INPUT);
+ }
con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
&mux, false, 0);
if (con < 0)
@@ -4370,37 +4409,21 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
/*
* pin fix-up
*/
-struct cxt_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
-{
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-
-}
-
-static void apply_pin_fixup(struct hda_codec *codec,
- const struct snd_pci_quirk *quirk,
- const struct cxt_pincfg **table)
-{
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (quirk) {
- snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
- quirk->name);
- apply_pincfg(codec, table[quirk->value]);
- }
-}
-
enum {
CXT_PINCFG_LENOVO_X200,
CXT_PINCFG_LENOVO_TP410,
+ CXT_FIXUP_STEREO_DMIC,
};
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+ spec->fixup_stereo_dmic = 1;
+}
+
/* ThinkPad X200 & co with cxt5051 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
{ 0x17, 0x21a11000 }, /* dock-mic */
{ 0x19, 0x2121103f }, /* dock-HP */
@@ -4409,16 +4432,26 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
};
/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
{ 0x19, 0x042110ff }, /* HP (seq# overridden) */
{ 0x1a, 0x21a190f0 }, /* dock-mic */
{ 0x1c, 0x212140ff }, /* dock-HP */
{}
};
-static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
- [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
- [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410,
+static const struct hda_fixup cxt_fixups[] = {
+ [CXT_PINCFG_LENOVO_X200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_x200,
+ },
+ [CXT_PINCFG_LENOVO_TP410] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_tp410,
+ },
+ [CXT_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ },
};
static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -4432,6 +4465,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
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, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
{}
};
@@ -4471,13 +4505,16 @@ static int patch_conexant_auto(struct hda_codec *codec)
case 0x14f15051:
add_cx5051_fake_mutes(codec);
codec->pin_amp_workaround = 1;
- apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl);
+ snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
break;
default:
codec->pin_amp_workaround = 1;
- apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl);
+ snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
+ break;
}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
/* Show mute-led control only on HP laptops
* This is a sort of white-list: on HP laptops, EAPD corresponds
* only to the mute-LED without actualy amp function. Meanwhile,
@@ -4556,6 +4593,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_conexant_auto },
{ .id = 0x14f150b9, .name = "CX20665",
.patch = patch_conexant_auto },
+ { .id = 0x14f1510f, .name = "CX20751/2",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15110, .name = "CX20751/2",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15111, .name = "CX20753/4",
+ .patch = patch_conexant_auto },
{} /* terminator */
};
@@ -4576,6 +4619,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab");
MODULE_ALIAS("snd-hda-codec-id:14f150ac");
MODULE_ALIAS("snd-hda-codec-id:14f150b8");
MODULE_ALIAS("snd-hda-codec-id:14f150b9");
+MODULE_ALIAS("snd-hda-codec-id:14f1510f");
+MODULE_ALIAS("snd-hda-codec-id:14f15110");
+MODULE_ALIAS("snd-hda-codec-id:14f15111");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 83f345f3c96..ad319d4dc32 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1592,10 +1592,10 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int dataDCC2, channel_id;
int i;
struct hdmi_spec *spec = codec->spec;
- struct hda_spdif_out *spdif =
- snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+ struct hda_spdif_out *spdif;
mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
chs = substream->runtime->channels;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 708d47c294e..ff71dcef08e 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -32,6 +32,7 @@
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
@@ -66,8 +67,6 @@ struct alc_customize_define {
unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
-struct alc_fixup;
-
struct alc_multi_io {
hda_nid_t pin; /* multi-io widget pin NID */
hda_nid_t dac; /* DAC to be connected */
@@ -82,19 +81,33 @@ enum {
#define MAX_VOL_NIDS 0x40
+/* make compatible with old code */
+#define alc_apply_pincfgs snd_hda_apply_pincfgs
+#define alc_apply_fixup snd_hda_apply_fixup
+#define alc_pick_fixup snd_hda_pick_fixup
+#define alc_fixup hda_fixup
+#define alc_pincfg hda_pintbl
+#define alc_model_fixup hda_model_fixup
+
+#define ALC_FIXUP_PINS HDA_FIXUP_PINS
+#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS
+#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC
+
+#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE
+#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE
+#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT
+#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
+
+
struct alc_spec {
+ struct hda_gen_spec gen;
+
/* codec parameterization */
const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
const struct snd_kcontrol_new *cap_mixer; /* capture mixer */
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- const struct hda_verb *init_verbs[10]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
char stream_name_analog[32]; /* analog PCM stream */
const struct hda_pcm_stream *stream_analog_playback;
const struct hda_pcm_stream *stream_analog_capture;
@@ -210,11 +223,6 @@ struct alc_spec {
unsigned int pll_coef_idx, pll_coef_bit;
unsigned int coef0;
- /* fix-up list */
- int fixup_id;
- const struct alc_fixup *fixup_list;
- const char *fixup_name;
-
/* multi-io */
int multi_ios;
struct alc_multi_io multi_io[4];
@@ -319,13 +327,16 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
/* for shared I/O, change the pin-control accordingly */
if (spec->shared_mic_hp) {
+ unsigned int val;
+ hda_nid_t pin = spec->autocfg.inputs[1].pin;
/* NOTE: this assumes that there are only two inputs, the
* first is the real internal mic and the second is HP jack.
*/
- snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_mux[adc_idx] ?
- PIN_VREF80 : PIN_HP);
+ if (spec->cur_mux[adc_idx])
+ val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
+ else
+ val = PIN_HP;
+ snd_hda_set_pin_ctl(codec, pin, val);
spec->automute_speaker = !spec->cur_mux[adc_idx];
call_update_outputs(codec);
}
@@ -338,7 +349,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
nid = get_capsrc(spec, adc_idx);
/* no selection? */
- num_conns = snd_hda_get_conn_list(codec, nid, NULL);
+ num_conns = snd_hda_get_num_conns(codec, nid);
if (num_conns <= 1)
return 1;
@@ -376,25 +387,9 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
int auto_pin_type)
{
unsigned int val = PIN_IN;
-
- if (auto_pin_type == AUTO_PIN_MIC) {
- unsigned int pincap;
- unsigned int oldval;
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- /* if the default pin setup is vref50, we give it priority */
- if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
- val = PIN_VREF80;
- else if (pincap & AC_PINCAP_VREF_50)
- val = PIN_VREF50;
- else if (pincap & AC_PINCAP_VREF_100)
- val = PIN_VREF100;
- else if (pincap & AC_PINCAP_VREF_GRD)
- val = PIN_VREFGRD;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ if (auto_pin_type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, nid);
+ snd_hda_set_pin_ctl(codec, nid, val);
}
/*
@@ -409,13 +404,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
spec->mixers[spec->num_mixers++] = mix;
}
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
- if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
- return;
- spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
/*
* GPIO setup tables, used in initialization
*/
@@ -517,9 +505,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
} else
val = 0;
val |= pin_bits;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
+ snd_hda_set_pin_ctl(codec, nid, val);
break;
case ALC_AUTOMUTE_AMP:
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
@@ -1200,6 +1186,16 @@ static void alc_auto_check_switches(struct hda_codec *codec)
*/
#define ALC_FIXUP_SKU_IGNORE (2)
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+ }
+}
+
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
@@ -1403,178 +1399,6 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
}
/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct alc_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-struct alc_model_fixup {
- const int id;
- const char *name;
-};
-
-struct alc_fixup {
- int type;
- bool chained;
- int chain_id;
- union {
- unsigned int sku;
- const struct alc_pincfg *pins;
- const struct hda_verb *verbs;
- void (*func)(struct hda_codec *codec,
- const struct alc_fixup *fix,
- int action);
- } v;
-};
-
-enum {
- ALC_FIXUP_INVALID,
- ALC_FIXUP_SKU,
- ALC_FIXUP_PINS,
- ALC_FIXUP_VERBS,
- ALC_FIXUP_FUNC,
-};
-
-enum {
- ALC_FIXUP_ACT_PRE_PROBE,
- ALC_FIXUP_ACT_PROBE,
- ALC_FIXUP_ACT_INIT,
- ALC_FIXUP_ACT_BUILD,
-};
-
-static void alc_apply_pincfgs(struct hda_codec *codec,
- const struct alc_pincfg *cfg)
-{
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
-
-static void alc_apply_fixup(struct hda_codec *codec, int action)
-{
- struct alc_spec *spec = codec->spec;
- int id = spec->fixup_id;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- const char *modelname = spec->fixup_name;
-#endif
- int depth = 0;
-
- if (!spec->fixup_list)
- return;
-
- while (id >= 0) {
- const struct alc_fixup *fix = spec->fixup_list + id;
- const struct alc_pincfg *cfg;
-
- switch (fix->type) {
- case ALC_FIXUP_SKU:
- if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply sku override for %s\n",
- codec->chip_name, modelname);
- spec->cdefine.sku_cfg = fix->v.sku;
- spec->cdefine.fixup = 1;
- break;
- case ALC_FIXUP_PINS:
- cfg = fix->v.pins;
- if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply pincfg for %s\n",
- codec->chip_name, modelname);
- alc_apply_pincfgs(codec, cfg);
- break;
- case ALC_FIXUP_VERBS:
- if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply fix-verbs for %s\n",
- codec->chip_name, modelname);
- add_verb(codec->spec, fix->v.verbs);
- break;
- case ALC_FIXUP_FUNC:
- if (!fix->v.func)
- break;
- snd_printdd(KERN_INFO "hda_codec: %s: "
- "Apply fix-func for %s\n",
- codec->chip_name, modelname);
- fix->v.func(codec, fix, action);
- break;
- default:
- snd_printk(KERN_ERR "hda_codec: %s: "
- "Invalid fixup type %d\n",
- codec->chip_name, fix->type);
- break;
- }
- if (!fix->chained)
- break;
- if (++depth > 10)
- break;
- id = fix->chain_id;
- }
-}
-
-static void alc_pick_fixup(struct hda_codec *codec,
- const struct alc_model_fixup *models,
- const struct snd_pci_quirk *quirk,
- const struct alc_fixup *fixlist)
-{
- struct alc_spec *spec = codec->spec;
- const struct snd_pci_quirk *q;
- int id = -1;
- const char *name = NULL;
-
- /* when model=nofixup is given, don't pick up any fixups */
- if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
- spec->fixup_list = NULL;
- spec->fixup_id = -1;
- return;
- }
-
- if (codec->modelname && models) {
- while (models->name) {
- if (!strcmp(codec->modelname, models->name)) {
- id = models->id;
- name = models->name;
- break;
- }
- models++;
- }
- }
- if (id < 0) {
- q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (q) {
- id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = q->name;
-#endif
- }
- }
- if (id < 0) {
- for (q = quirk; q->subvendor; q++) {
- unsigned int vendorid =
- q->subdevice | (q->subvendor << 16);
- if (vendorid == codec->subsystem_id) {
- id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = q->name;
-#endif
- break;
- }
- }
- }
-
- spec->fixup_id = id;
- if (id >= 0) {
- spec->fixup_list = fixlist;
- spec->fixup_name = name;
- }
-}
-
-/*
* COEF access helper functions
*/
static int alc_read_coef_idx(struct hda_codec *codec,
@@ -1621,8 +1445,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
pin = spec->autocfg.dig_out_pins[i];
if (!pin)
continue;
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
if (!i)
dac = spec->multiout.dig_out_nid;
else
@@ -1635,9 +1458,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
}
pin = spec->autocfg.dig_in_pin;
if (pin)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_IN);
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN);
}
/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
@@ -2068,7 +1889,6 @@ static void alc_auto_init_std(struct hda_codec *codec);
static int alc_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int i;
if (spec->init_hook)
spec->init_hook(codec);
@@ -2076,8 +1896,6 @@ static int alc_init(struct hda_codec *codec)
alc_fix_pll(codec);
alc_auto_init_amp(codec, spec->init_amp);
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
alc_init_special_input_src(codec);
alc_auto_init_std(codec);
@@ -2725,7 +2543,6 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
hda_nid_t src;
- const hda_nid_t *list;
unsigned int caps = get_wcaps(codec, nid);
int type = get_wcaps_type(caps);
@@ -2743,13 +2560,14 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
cap_nids[nums] = src;
break;
}
- n = snd_hda_get_conn_list(codec, src, &list);
+ n = snd_hda_get_num_conns(codec, src);
if (n > 1) {
cap_nids[nums] = src;
break;
} else if (n != 1)
break;
- src = *list;
+ if (snd_hda_get_connections(codec, src, &src, 1) != 1)
+ break;
}
if (++nums >= max_nums)
break;
@@ -2856,8 +2674,7 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_type)
{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
+ snd_hda_set_pin_ctl(codec, nid, pin_type);
/* unmute pin */
if (nid_has_mute(codec, nid, HDA_OUTPUT))
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -2891,7 +2708,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
/* mute all loopback inputs */
if (spec->mixer_nid) {
- int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+ int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
for (i = 0; i < nums; i++)
snd_hda_codec_write(codec, spec->mixer_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -3521,7 +3338,7 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
type = ALC_CTL_WIDGET_MUTE;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
- } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
+ } else if (snd_hda_get_num_conns(codec, nid) == 1) {
type = ALC_CTL_WIDGET_MUTE;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
} else {
@@ -3998,9 +3815,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
if (output) {
- snd_hda_codec_update_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
+ snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
HDA_AMP_MUTE, 0);
@@ -4009,9 +3824,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_update_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->multi_io[idx].ctl_in);
+ snd_hda_set_pin_ctl_cache(codec, nid,
+ spec->multi_io[idx].ctl_in);
}
return 0;
}
@@ -4084,7 +3898,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
nums = 0;
for (n = 0; n < spec->num_adc_nids; n++) {
hda_nid_t cap = spec->private_capsrc_nids[n];
- int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
+ int num_conns = snd_hda_get_num_conns(codec, cap);
for (i = 0; i < imux->num_items; i++) {
hda_nid_t pin = spec->imux_pins[i];
if (pin) {
@@ -4213,7 +4027,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
HDA_AMP_MUTE, 0);
- } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
+ } else if (snd_hda_get_num_conns(codec, cap) > 1) {
snd_hda_codec_write_cache(codec, cap, 0,
AC_VERB_SET_CONNECT_SEL, idx);
}
@@ -4427,6 +4241,25 @@ static int alc_parse_auto_config(struct hda_codec *codec,
return 1;
}
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+ struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ int err;
+
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->mixer_nid = mixer_nid;
+
+ err = alc_codec_rename_from_preset(codec);
+ if (err < 0) {
+ kfree(spec);
+ return err;
+ }
+ return 0;
+}
+
static int alc880_parse_auto_config(struct hda_codec *codec)
{
static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
@@ -4808,13 +4641,11 @@ static int patch_alc880(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
spec->need_dac_fix = 1;
alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
@@ -4890,7 +4721,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
spec->unsol_event = alc_sku_unsol_event;
- add_verb(codec->spec, alc_gpio1_init_verbs);
+ snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
}
}
@@ -5001,13 +4832,11 @@ static int patch_alc260(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x07;
+ spec = codec->spec;
alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5171,8 +5000,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
val = snd_hda_codec_read(codec, nids[i], 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
val |= AC_PINCTL_VREF_80;
- snd_hda_codec_write(codec, nids[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ snd_hda_set_pin_ctl(codec, nids[i], val);
spec->keep_vref_in_automute = 1;
break;
}
@@ -5193,8 +5021,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
val = snd_hda_codec_read(codec, nids[i], 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
val |= AC_PINCTL_VREF_50;
- snd_hda_codec_write(codec, nids[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ snd_hda_set_pin_ctl(codec, nids[i], val);
}
spec->keep_vref_in_automute = 1;
}
@@ -5225,8 +5052,8 @@ static const struct alc_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_ACER_ASPIRE_7736] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC882_FIXUP_ASUS_W90V] = {
.type = ALC_FIXUP_PINS,
@@ -5476,13 +5303,11 @@ static int patch_alc882(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
switch (codec->vendor_id) {
case 0x10ec0882:
@@ -5494,10 +5319,6 @@ static int patch_alc882(struct hda_codec *codec)
break;
}
- err = alc_codec_rename_from_preset(codec);
- if (err < 0)
- goto error;
-
alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
alc882_fixups);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5621,13 +5442,11 @@ static int patch_alc262(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
#if 0
/* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
@@ -5710,7 +5529,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
if (err > 0) {
if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
add_mixer(spec, alc268_beep_mixer);
- add_verb(spec, alc268_beep_init_verbs);
+ snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
}
}
return err;
@@ -5723,13 +5542,12 @@ static int patch_alc268(struct hda_codec *codec)
struct alc_spec *spec;
int i, has_beep, err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
/* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
/* automatic parse from the BIOS config */
err = alc268_parse_auto_config(codec);
@@ -5946,9 +5764,7 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled)
{
struct hda_codec *codec = private_data;
unsigned int pinval = enabled ? 0x20 : 0x24;
- snd_hda_codec_update_cache(codec, 0x19, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinval);
+ snd_hda_set_pin_ctl_cache(codec, 0x19, pinval);
}
static void alc269_fixup_mic2_mute(struct hda_codec *codec,
@@ -6015,8 +5831,8 @@ static const struct alc_fixup alc269_fixups[] = {
}
},
[ALC269_FIXUP_SKU_IGNORE] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC269_FIXUP_ASUS_G73JW] = {
.type = ALC_FIXUP_PINS,
@@ -6242,19 +6058,13 @@ static void alc269_fill_coef(struct hda_codec *codec)
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err = 0;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->mixer_nid = 0x0b;
+ int err;
- err = alc_codec_rename_from_preset(codec);
+ err = alc_alloc_spec(codec, 0x0b);
if (err < 0)
- goto error;
+ return err;
+
+ spec = codec->spec;
if (codec->vendor_id == 0x10ec0269) {
spec->codec_variant = ALC269_TYPE_ALC269VA;
@@ -6346,8 +6156,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
val |= AC_PINCTL_IN_EN;
val |= AC_PINCTL_VREF_50;
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ snd_hda_set_pin_ctl(codec, 0x0f, val);
spec->keep_vref_in_automute = 1;
}
@@ -6401,13 +6210,11 @@ static int patch_alc861(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x15;
+ spec = codec->spec;
alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6504,13 +6311,11 @@ static int patch_alc861vd(struct hda_codec *codec)
struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6522,7 +6327,7 @@ static int patch_alc861vd(struct hda_codec *codec)
if (codec->vendor_id == 0x10ec0660) {
/* always turn on EAPD */
- add_verb(spec, alc660vd_eapd_verbs);
+ snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
}
if (!spec->no_analog) {
@@ -6635,8 +6440,8 @@ static const struct alc_fixup alc662_fixups[] = {
}
},
[ALC662_FIXUP_SKU_IGNORE] = {
- .type = ALC_FIXUP_SKU,
- .v.sku = ALC_FIXUP_SKU_IGNORE,
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
},
[ALC662_FIXUP_HP_RP5800] = {
.type = ALC_FIXUP_PINS,
@@ -6849,25 +6654,19 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
- int err = 0;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
+ int err;
- codec->spec = spec;
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
- spec->mixer_nid = 0x0b;
+ spec = codec->spec;
/* handle multiple HPs as is */
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- err = alc_codec_rename_from_preset(codec);
- if (err < 0)
- goto error;
-
if ((alc_get_coef0(codec) & (1 << 14)) &&
codec->bus->pci->subsystem_vendor == 0x1025 &&
spec->cdefine.platform_type == 1) {
@@ -6930,16 +6729,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
*/
static int patch_alc680(struct hda_codec *codec)
{
- struct alc_spec *spec;
int err;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
/* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
/* automatic parse from the BIOS config */
err = alc680_parse_auto_config(codec);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 2cb1e08f962..7db8228f1b8 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -36,6 +36,7 @@
#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
@@ -221,6 +222,7 @@ struct sigmatel_spec {
unsigned char aloopback_shift;
/* power management */
+ unsigned int power_map_bits;
unsigned int num_pwrs;
const hda_nid_t *pwr_nids;
const hda_nid_t *dac_list;
@@ -314,6 +316,9 @@ struct sigmatel_spec {
struct hda_vmaster_mute_hook vmaster_mute;
};
+#define AC_VERB_IDT_SET_POWER_MAP 0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP 0xfec
+
static const hda_nid_t stac9200_adc_nids[1] = {
0x03,
};
@@ -681,8 +686,7 @@ static int stac_vrefout_set(struct hda_codec *codec,
pinctl &= ~AC_PINCTL_VREFEN;
pinctl |= (new_vref & AC_PINCTL_VREFEN);
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
if (error < 0)
return error;
@@ -706,8 +710,7 @@ static unsigned int stac92xx_vref_set(struct hda_codec *codec,
else
pincfg |= AC_PINCTL_IN_EN;
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg);
if (error < 0)
return error;
else
@@ -2505,27 +2508,10 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
return 0;
}
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
- hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- if (pincap & AC_PINCAP_VREF_100)
- return AC_PINCTL_VREF_100;
- if (pincap & AC_PINCAP_VREF_80)
- return AC_PINCTL_VREF_80;
- if (pincap & AC_PINCAP_VREF_50)
- return AC_PINCTL_VREF_50;
- if (pincap & AC_PINCAP_VREF_GRD)
- return AC_PINCTL_VREF_GRD;
- return 0;
-}
-
static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
{
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_type);
}
#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
@@ -2594,7 +2580,7 @@ static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
hda_nid_t nid = kcontrol->private_value;
unsigned int vref = stac92xx_vref_get(codec, nid);
- if (vref == stac92xx_get_default_vref(codec, nid))
+ if (vref == snd_hda_get_default_vref(codec, nid))
ucontrol->value.enumerated.item[0] = 0;
else if (vref == AC_PINCTL_VREF_GRD)
ucontrol->value.enumerated.item[0] = 1;
@@ -2613,7 +2599,7 @@ static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
hda_nid_t nid = kcontrol->private_value;
if (ucontrol->value.enumerated.item[0] == 0)
- new_vref = stac92xx_get_default_vref(codec, nid);
+ new_vref = snd_hda_get_default_vref(codec, nid);
else if (ucontrol->value.enumerated.item[0] == 1)
new_vref = AC_PINCTL_VREF_GRD;
else if (ucontrol->value.enumerated.item[0] == 2)
@@ -2679,7 +2665,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
else {
unsigned int pinctl = AC_PINCTL_IN_EN;
if (io_idx) /* set VREF for mic */
- pinctl |= stac92xx_get_default_vref(codec, nid);
+ pinctl |= snd_hda_get_default_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
@@ -2847,7 +2833,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
char name[22];
if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+ if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
&& nid == spec->line_switch)
control = STAC_CTL_WIDGET_IO_SWITCH;
else if (snd_hda_query_pin_caps(codec, nid)
@@ -4250,13 +4236,6 @@ static void stac_store_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "eapd_switch");
if (val >= 0)
spec->eapd_switch = val;
- get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
- if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- if (spec->gpio_led_polarity)
- spec->gpio_data |= spec->gpio_led;
- }
}
static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
@@ -4354,7 +4333,7 @@ static int stac92xx_init(struct hda_codec *codec)
unsigned int pinctl, conf;
if (type == AUTO_PIN_MIC) {
/* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
+ pinctl = snd_hda_get_default_vref(codec, nid);
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
@@ -4390,10 +4369,18 @@ static int stac92xx_init(struct hda_codec *codec)
hda_nid_t nid = spec->pwr_nids[i];
int pinctl, def_conf;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ def_conf = get_defcfg_connect(def_conf);
+ if (def_conf == AC_JACK_PORT_NONE) {
+ /* power off unused ports */
+ stac_toggle_power_map(codec, nid, 0);
+ continue;
+ }
/* power on when no jack detection is available */
/* or when the VREF is used for controlling LED */
if (!spec->hp_detect ||
- spec->vref_mute_led_nid == nid) {
+ spec->vref_mute_led_nid == nid ||
+ !is_jack_detectable(codec, nid)) {
stac_toggle_power_map(codec, nid, 1);
continue;
}
@@ -4411,15 +4398,6 @@ static int stac92xx_init(struct hda_codec *codec)
stac_toggle_power_map(codec, nid, 1);
continue;
}
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- def_conf = get_defcfg_connect(def_conf);
- /* skip any ports that don't have jacks since presence
- * detection is useless */
- if (def_conf != AC_JACK_PORT_NONE &&
- !is_jack_detectable(codec, nid)) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
stac_issue_unsol_event(codec, nid);
continue;
@@ -4432,6 +4410,12 @@ static int stac92xx_init(struct hda_codec *codec)
/* sync mute LED */
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+ /* sync the power-map */
+ if (spec->num_pwrs)
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;
@@ -4460,8 +4444,7 @@ static void stac92xx_shutup_pins(struct hda_codec *codec)
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
- snd_hda_codec_write(codec, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, pin->nid, 0);
}
}
@@ -4517,9 +4500,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl |= flag;
if (old_ctl != pin_ctl)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl);
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl);
}
static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4528,9 +4509,7 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
if (pin_ctl & flag)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl & ~flag);
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag);
}
static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
@@ -4682,14 +4661,18 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
idx = 1 << idx;
- val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
+ val = spec->power_map_bits;
if (enable)
val &= ~idx;
else
val |= idx;
/* power down unused output ports */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+ if (val != spec->power_map_bits) {
+ spec->power_map_bits = val;
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP, val);
+ }
}
static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
@@ -4866,6 +4849,11 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
struct sigmatel_spec *spec = codec->spec;
const struct dmi_device *dev = NULL;
+ if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ get_int_hint(codec, "gpio_led_polarity",
+ &spec->gpio_led_polarity);
+ return 1;
+ }
if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
NULL, dev))) {
@@ -4952,7 +4940,8 @@ static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
{
if (nid == codec->afg)
snd_iprintf(buffer, "Power-Map: 0x%02x\n",
- snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_IDT_GET_POWER_MAP, 0));
}
static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -5009,20 +4998,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
return 0;
}
-static int stac92xx_pre_resume(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- /* sync mute LED */
- if (spec->vref_mute_led_nid)
- stac_vrefout_set(codec, spec->vref_mute_led_nid,
- spec->vref_led);
- else if (spec->gpio_led)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data);
- return 0;
-}
-
static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{
@@ -5046,7 +5021,6 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
#else
#define stac92xx_suspend NULL
#define stac92xx_resume NULL
-#define stac92xx_pre_resume NULL
#define stac92xx_set_power_state NULL
#endif /* CONFIG_PM */
@@ -5592,9 +5566,6 @@ again:
codec->patch_ops.set_power_state =
stac92xx_set_power_state;
}
-#ifdef CONFIG_PM
- codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
}
err = stac92xx_parse_auto_config(codec);
@@ -5901,9 +5872,6 @@ again:
codec->patch_ops.set_power_state =
stac92xx_set_power_state;
}
-#ifdef CONFIG_PM
- codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
}
spec->multiout.dac_nids = spec->dac_nids;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 06214fdc948..82b368068e0 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -54,6 +54,7 @@
#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_auto_parser.h"
#include "hda_jack.h"
/* Pin Widget NID */
@@ -484,7 +485,7 @@ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
if (!path)
return;
- num = snd_hda_get_conn_list(codec, mix_nid, NULL);
+ num = snd_hda_get_num_conns(codec, mix_nid);
for (i = 0; i < num; i++) {
if (i == idx)
val = AMP_IN_UNMUTE(i);
@@ -532,8 +533,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
{
if (!pin)
return;
- snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
+ snd_hda_set_pin_ctl(codec, pin, pin_type);
if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
@@ -662,12 +662,12 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
hda_nid_t nid = cfg->inputs[i].pin;
if (spec->smart51_enabled && is_smart51_pins(codec, nid))
ctl = PIN_OUT;
- else if (cfg->inputs[i].type == AUTO_PIN_MIC)
- ctl = PIN_VREF50;
- else
+ else {
ctl = PIN_IN;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ ctl |= snd_hda_get_default_vref(codec, nid);
+ }
+ snd_hda_set_pin_ctl(codec, nid, ctl);
}
/* init input-src */
@@ -1006,9 +1006,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
+ snd_hda_set_pin_ctl(codec, nid, parm);
if (out_in == AC_PINCTL_OUT_EN) {
mute_aa_path(codec, 1);
notify_aa_path_ctls(codec);
@@ -1647,8 +1645,7 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
parm &= ~AC_PINCTL_OUT_EN;
else
parm |= AC_PINCTL_OUT_EN;
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
+ snd_hda_set_pin_ctl(codec, pins[i], parm);
}
}
@@ -1709,8 +1706,7 @@ static void via_gpio_control(struct hda_codec *codec)
if (gpio_data == 0x02) {
/* unmute line out */
- snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0],
PIN_OUT);
if (vol_counter & 0x20) {
/* decrease volume */
@@ -1728,9 +1724,7 @@ static void via_gpio_control(struct hda_codec *codec)
}
} else if (!(gpio_data & 0x02)) {
/* mute line out */
- snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- 0);
+ snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0);
}
}
@@ -2757,8 +2751,7 @@ static void via_auto_init_dig_in(struct hda_codec *codec)
struct via_spec *spec = codec->spec;
if (!spec->dig_in_nid)
return;
- snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+ snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
}
/* initialize the unsolicited events */
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 132a86e09d0..5be2e120a14 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2803,22 +2803,11 @@ static void __devexit snd_ice1712_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver ice1712_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ice1712_ids,
.probe = snd_ice1712_probe,
.remove = __devexit_p(snd_ice1712_remove),
};
-static int __init alsa_card_ice1712_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1712_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
+module_pci_driver(ice1712_driver);
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 812d10e43ae..a01a00d1cf4 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2873,7 +2873,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
}
#endif
-static struct pci_driver driver = {
+static struct pci_driver vt1724_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
@@ -2884,15 +2884,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_ice1724_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1724_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1724_init)
-module_exit(alsa_card_ice1724_exit)
+module_pci_driver(vt1724_driver);
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index e0a4263baa2..f4e2dd4da8c 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -3338,7 +3338,7 @@ static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver intel8x0_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_intel8x0_ids,
.probe = snd_intel8x0_probe,
@@ -3349,16 +3349,4 @@ static struct pci_driver driver = {
#endif
};
-
-static int __init alsa_card_intel8x0_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0_init)
-module_exit(alsa_card_intel8x0_exit)
+module_pci_driver(intel8x0_driver);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index d689913a61b..fc27a6a69e7 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1324,7 +1324,7 @@ static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver intel8x0m_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_intel8x0m_ids,
.probe = snd_intel8x0m_probe,
@@ -1335,16 +1335,4 @@ static struct pci_driver driver = {
#endif
};
-
-static int __init alsa_card_intel8x0m_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0m_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0m_init)
-module_exit(alsa_card_intel8x0m_exit)
+module_pci_driver(intel8x0m_driver);
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 8fea45ab588..e69ce5f9c31 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2476,22 +2476,11 @@ static void __devexit snd_korg1212_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver korg1212_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_korg1212_ids,
.probe = snd_korg1212_probe,
.remove = __devexit_p(snd_korg1212_remove),
};
-static int __init alsa_card_korg1212_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_korg1212_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_korg1212_init)
-module_exit(alsa_card_korg1212_exit)
+module_pci_driver(korg1212_driver);
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
index 37598273685..ac15166bee6 100644
--- a/sound/pci/lola/lola.c
+++ b/sound/pci/lola/lola.c
@@ -770,22 +770,11 @@ static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
MODULE_DEVICE_TABLE(pci, lola_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver lola_driver = {
.name = KBUILD_MODNAME,
.id_table = lola_ids,
.probe = lola_probe,
.remove = __devexit_p(lola_remove),
};
-static int __init alsa_card_lola_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_lola_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_lola_init)
-module_exit(alsa_card_lola_exit)
+module_pci_driver(lola_driver);
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index d94c0c292bd..d1ab4370673 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -1141,24 +1141,11 @@ static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
}
-static struct pci_driver driver = {
+static struct pci_driver lx6464es_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_lx6464es_ids,
.probe = snd_lx6464es_probe,
.remove = __devexit_p(snd_lx6464es_remove),
};
-
-/* module initialization */
-static int __init mod_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit mod_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_pci_driver(lx6464es_driver);
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 78229b0dad2..deef2139958 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2837,7 +2837,7 @@ static void __devexit snd_m3_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver m3_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_m3_ids,
.probe = snd_m3_probe,
@@ -2848,15 +2848,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_m3_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_m3_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_m3_init)
-module_exit(alsa_card_m3_exit)
+module_pci_driver(m3_driver);
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 487837c01c9..0762610c99c 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1380,22 +1380,11 @@ static void __devexit snd_mixart_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver mixart_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_mixart_ids,
.probe = snd_mixart_probe,
.remove = __devexit_p(snd_mixart_remove),
};
-static int __init alsa_card_mixart_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_mixart_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_mixart_init)
-module_exit(alsa_card_mixart_exit)
+module_pci_driver(mixart_driver);
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index ade2c64bd60..8159b05ee94 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1742,7 +1742,7 @@ static void __devexit snd_nm256_remove(struct pci_dev *pci)
}
-static struct pci_driver driver = {
+static struct pci_driver nm256_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_nm256_ids,
.probe = snd_nm256_probe,
@@ -1753,16 +1753,4 @@ static struct pci_driver driver = {
#endif
};
-
-static int __init alsa_card_nm256_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_nm256_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_nm256_init)
-module_exit(alsa_card_nm256_exit)
+module_pci_driver(nm256_driver);
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index eab663eef11..610275bfbae 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -94,6 +94,7 @@ enum {
MODEL_2CH_OUTPUT,
MODEL_HG2PCI,
MODEL_XONAR_DG,
+ MODEL_XONAR_DGX,
};
static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
@@ -109,6 +110,8 @@ static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
/* Asus Xonar DG */
{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
+ /* Asus Xonar DGX */
+ { OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
/* PCI 2.0 HD Audio */
{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
/* Kuroutoshikou CMI8787-HG2PCI */
@@ -827,6 +830,11 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
break;
case MODEL_XONAR_DG:
chip->model = model_xonar_dg;
+ chip->model.shortname = "Xonar DG";
+ break;
+ case MODEL_XONAR_DGX:
+ chip->model = model_xonar_dg;
+ chip->model.shortname = "Xonar DGX";
break;
}
if (id->driver_data == MODEL_MERIDIAN ||
@@ -870,15 +878,4 @@ static struct pci_driver oxygen_driver = {
#endif
};
-static int __init alsa_card_oxygen_init(void)
-{
- return pci_register_driver(&oxygen_driver);
-}
-
-static void __exit alsa_card_oxygen_exit(void)
-{
- pci_unregister_driver(&oxygen_driver);
-}
-
-module_init(alsa_card_oxygen_init)
-module_exit(alsa_card_oxygen_exit)
+module_pci_driver(oxygen_driver);
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 3fdee495017..19962c6d38c 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -100,15 +100,4 @@ static struct pci_driver xonar_driver = {
.shutdown = oxygen_pci_shutdown,
};
-static int __init alsa_card_xonar_init(void)
-{
- return pci_register_driver(&xonar_driver);
-}
-
-static void __exit alsa_card_xonar_exit(void)
-{
- pci_unregister_driver(&xonar_driver);
-}
-
-module_init(alsa_card_xonar_init)
-module_exit(alsa_card_xonar_exit)
+module_pci_driver(xonar_driver);
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
index 793bdf03d7e..77acd790ea4 100644
--- a/sound/pci/oxygen/xonar_dg.c
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -1,5 +1,5 @@
/*
- * card driver for the Xonar DG
+ * card driver for the Xonar DG/DGX
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
@@ -17,8 +17,8 @@
*/
/*
- * Xonar DG
- * --------
+ * Xonar DG/DGX
+ * ------------
*
* CMI8788:
*
@@ -581,7 +581,6 @@ static void dump_cs4245_registers(struct oxygen *chip,
}
struct oxygen_model model_xonar_dg = {
- .shortname = "Xonar DG",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8786",
.init = dg_init,
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index fd1809ab73b..0435f45e951 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1607,22 +1607,11 @@ static void __devexit pcxhr_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver pcxhr_driver = {
.name = KBUILD_MODNAME,
.id_table = pcxhr_ids,
.probe = pcxhr_probe,
.remove = __devexit_p(pcxhr_remove),
};
-static int __init pcxhr_module_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit pcxhr_module_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(pcxhr_module_init)
-module_exit(pcxhr_module_exit)
+module_pci_driver(pcxhr_driver);
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 0481d94aac9..cbeb3f77350 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1837,8 +1837,7 @@ static int snd_riptide_free(struct snd_riptide *chip)
}
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->fw_entry)
- release_firmware(chip->fw_entry);
+ release_firmware(chip->fw_entry);
release_and_free_resource(chip->res_port);
kfree(chip);
return 0;
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index b4819d5e41d..46b3629dda2 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1984,22 +1984,11 @@ static void __devexit snd_rme32_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver rme32_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme32_ids,
.probe = snd_rme32_probe,
.remove = __devexit_p(snd_rme32_remove),
};
-static int __init alsa_card_rme32_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme32_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme32_init)
-module_exit(alsa_card_rme32_exit)
+module_pci_driver(rme32_driver);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index ba894158e76..9b98dc40698 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -2395,22 +2395,11 @@ static void __devexit snd_rme96_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver rme96_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme96_ids,
.probe = snd_rme96_probe,
.remove = __devexit_p(snd_rme96_remove),
};
-static int __init alsa_card_rme96_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme96_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme96_init)
-module_exit(alsa_card_rme96_exit)
+module_pci_driver(rme96_driver);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 0b2aea2ce17..0d6930c4f4b 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5636,22 +5636,11 @@ static void __devexit snd_hdsp_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver hdsp_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_hdsp_ids,
.probe = snd_hdsp_probe,
.remove = __devexit_p(snd_hdsp_remove),
};
-static int __init alsa_card_hdsp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdsp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdsp_init)
-module_exit(alsa_card_hdsp_exit)
+module_pci_driver(hdsp_driver);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index bc030a2088d..0a5027b9471 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6918,23 +6918,11 @@ static void __devexit snd_hdspm_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver hdspm_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_hdspm_ids,
.probe = snd_hdspm_probe,
.remove = __devexit_p(snd_hdspm_remove),
};
-
-static int __init alsa_card_hdspm_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdspm_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdspm_init)
-module_exit(alsa_card_hdspm_exit)
+module_pci_driver(hdspm_driver);
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index b737d1619cc..a15fc100ab0 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2631,22 +2631,11 @@ static void __devexit snd_rme9652_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver rme9652_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_rme9652_ids,
.probe = snd_rme9652_probe,
.remove = __devexit_p(snd_rme9652_remove),
};
-static int __init alsa_card_hammerfall_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hammerfall_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hammerfall_init)
-module_exit(alsa_card_hammerfall_exit)
+module_pci_driver(rme9652_driver);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index ff500a87f76..1552642765d 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1488,15 +1488,4 @@ static struct pci_driver sis7019_driver = {
#endif
};
-static int __init sis7019_init(void)
-{
- return pci_register_driver(&sis7019_driver);
-}
-
-static void __exit sis7019_exit(void)
-{
- pci_unregister_driver(&sis7019_driver);
-}
-
-module_init(sis7019_init);
-module_exit(sis7019_exit);
+module_pci_driver(sis7019_driver);
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 54cc802050f..baa9946bedf 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1530,22 +1530,11 @@ static void __devexit snd_sonic_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver sonicvibes_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_sonic_ids,
.probe = snd_sonic_probe,
.remove = __devexit_p(snd_sonic_remove),
};
-static int __init alsa_card_sonicvibes_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_sonicvibes_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_sonicvibes_init)
-module_exit(alsa_card_sonicvibes_exit)
+module_pci_driver(sonicvibes_driver);
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 5f1def7f45e..611983ec732 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -172,7 +172,7 @@ static void __devexit snd_trident_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver trident_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_trident_ids,
.probe = snd_trident_probe,
@@ -183,15 +183,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_trident_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_trident_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_trident_init)
-module_exit(alsa_card_trident_exit)
+module_pci_driver(trident_driver);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 75630408c6d..b5afab48943 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -2619,7 +2619,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver via82xx_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_via82xx_ids,
.probe = snd_via82xx_probe,
@@ -2630,15 +2630,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_via82xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_driver);
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 5efcbcac506..59fd47ed0a3 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1223,7 +1223,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver via82xx_modem_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_via82xx_modem_ids,
.probe = snd_via82xx_probe,
@@ -1234,15 +1234,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_via82xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_modem_driver);
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 6a534bfe127..1ea1f656a5d 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -289,7 +289,7 @@ static int snd_vx222_resume(struct pci_dev *pci)
}
#endif
-static struct pci_driver driver = {
+static struct pci_driver vx222_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_vx222_ids,
.probe = snd_vx222_probe,
@@ -300,15 +300,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_vx222_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_vx222_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vx222_init)
-module_exit(alsa_card_vx222_exit)
+module_pci_driver(vx222_driver);
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 94ab728f5ca..9a1d01d653a 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -350,7 +350,7 @@ static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_driver driver = {
+static struct pci_driver ymfpci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ymfpci_ids,
.probe = snd_card_ymfpci_probe,
@@ -361,15 +361,4 @@ static struct pci_driver driver = {
#endif
};
-static int __init alsa_card_ymfpci_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ymfpci_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ymfpci_init)
-module_exit(alsa_card_ymfpci_exit)
+module_pci_driver(ymfpci_driver);
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index b11f82b5718..f8b01c77b29 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -433,7 +433,7 @@ probe_error:
/*
* "driver" definition
*/
-static struct platform_driver driver = {
+static struct platform_driver sh_dac_driver = {
.probe = snd_sh_dac_probe,
.remove = snd_sh_dac_remove,
.driver = {
@@ -441,4 +441,4 @@ static struct platform_driver driver = {
},
};
-module_platform_driver(driver);
+module_platform_driver(sh_dac_driver);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 91c985599d3..40b2ad1bb1c 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,7 +35,6 @@ source "sound/soc/blackfin/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/ep93xx/Kconfig"
source "sound/soc/fsl/Kconfig"
-source "sound/soc/imx/Kconfig"
source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
@@ -48,9 +47,13 @@ source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/ux500/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
+# generic frame-work
+source "sound/soc/generic/Kconfig"
+
endif # SND_SOC
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2feaf376e94..70990f4017f 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -6,13 +6,13 @@ obj-$(CONFIG_SND_SOC_DMAENGINE_PCM) += snd-soc-dmaengine-pcm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
+obj-$(CONFIG_SND_SOC) += generic/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += ep93xx/
obj-$(CONFIG_SND_SOC) += fsl/
-obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += mid-x86/
obj-$(CONFIG_SND_SOC) += mxs/
@@ -25,3 +25,4 @@ obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index b39ad356b92..7dbeef1099b 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -44,16 +44,8 @@
static struct snd_soc_card bf5xx_ssm2602;
-static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int clk = 0;
- int ret = 0;
-
- pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
- params_format(params));
/*
* If you are using a crystal source which frequency is not 12MHz
* then modify the below case statement with frequency of the crystal.
@@ -61,31 +53,10 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
* If you are using the SPORT to generate clocking then this is
* where to do it.
*/
-
- switch (params_rate(params)) {
- case 8000:
- case 16000:
- case 48000:
- case 96000:
- case 11025:
- case 22050:
- case 44100:
- clk = 12000000;
- break;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+ return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000,
SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- return 0;
}
-static struct snd_soc_ops bf5xx_ssm2602_ops = {
- .hw_params = bf5xx_ssm2602_hw_params,
-};
-
/* CODEC is master for BCLK and LRC in this configuration. */
#define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
SND_SOC_DAIFMT_CBM_CFM)
@@ -98,7 +69,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ssm2602.0-001b",
- .ops = &bf5xx_ssm2602_ops,
+ .init = bf5xx_ssm2602_dai_init,
.dai_fmt = BF5XX_SSM2602_DAIFMT,
},
{
@@ -108,7 +79,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ssm2602.0-001b",
- .ops = &bf5xx_ssm2602_ops,
+ .init = bf5xx_ssm2602_dai_init,
.dai_fmt = BF5XX_SSM2602_DAIFMT,
},
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 59d8efaa17e..1e1613a438d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -29,6 +29,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ALC5632 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS42L51 if I2C
+ select SND_SOC_CS42L52 if I2C
select SND_SOC_CS42L73 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
@@ -37,11 +38,15 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DFBMCS320
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
+ select SND_SOC_LM49453 if I2C
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9768 if I2C
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_PCM3008
select SND_SOC_RT5631 if I2C
select SND_SOC_SGTL5000 if I2C
@@ -181,6 +186,9 @@ config SND_SOC_CQ0093VC
config SND_SOC_CS42L51
tristate
+config SND_SOC_CS42L52
+ tristate
+
config SND_SOC_CS42L73
tristate
@@ -217,6 +225,9 @@ config SND_SOC_DFBMCS320
config SND_SOC_DMIC
tristate
+config SND_SOC_LM49453
+ tristate
+
config SND_SOC_MAX98088
tristate
@@ -226,6 +237,9 @@ config SND_SOC_MAX98095
config SND_SOC_MAX9850
tristate
+config SND_SOC_OMAP_HDMI_CODEC
+ tristate
+
config SND_SOC_PCM3008
tristate
@@ -435,5 +449,11 @@ config SND_SOC_MAX9768
config SND_SOC_MAX9877
tristate
+config SND_SOC_MC13783
+ tristate
+
+config SND_SOC_ML26124
+ tristate
+
config SND_SOC_TPA6130A2
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6662eb0cdcc..fc27fec3948 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs42l51-objs := cs42l51.o
+snd-soc-cs42l52-objs := cs42l52.o
snd-soc-cs42l73-objs := cs42l73.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cs4271-objs := cs4271.o
@@ -25,10 +26,14 @@ snd-soc-dmic-objs := dmic.o
snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
+snd-soc-lm49453-objs := lm49453.o
snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
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-pcm3008-objs := pcm3008.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-sgtl5000-objs := sgtl5000.o
@@ -121,6 +126,7 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
+obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
@@ -128,13 +134,17 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
-obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_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_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 1bbad4c16d2..2023c749f23 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -26,13 +26,11 @@
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
+ return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
}
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 12e3b411855..c67b50d8b31 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -162,9 +162,7 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int word_len = 0;
-
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
/* bit size */
switch (params_format(params)) {
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index a4a6bef2c0b..13e62be4f99 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -245,9 +245,7 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int word_len = 0, master_rate = 0;
-
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
/* bit size */
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 78e9ce48bb9..3d50fc8646b 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -258,8 +258,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
snd_pcm_format_t format;
unsigned int val;
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index ceb96ecf558..31d4483245d 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -88,8 +88,7 @@ static int ak4104_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 snd_soc_codec *codec = dai->codec;
int val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 838ae8b22b5..618fdc30f73 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -262,8 +262,7 @@ static int ak4535_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 snd_soc_codec *codec = dai->codec;
struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index c4d165a4bdd..543a12f471b 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -296,8 +296,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int rate = params_rate(params), fs = 256;
u8 mode2;
@@ -517,67 +516,24 @@ static int ak4641_resume(struct snd_soc_codec *codec)
static int ak4641_probe(struct snd_soc_codec *codec)
{
- struct ak4641_platform_data *pdata = codec->dev->platform_data;
int ret;
-
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- ret = gpio_request_one(pdata->gpio_power,
- GPIOF_OUT_INIT_LOW, "ak4641 power");
- if (ret)
- goto err_out;
- }
- if (gpio_is_valid(pdata->gpio_npdn)) {
- ret = gpio_request_one(pdata->gpio_npdn,
- GPIOF_OUT_INIT_LOW, "ak4641 npdn");
- if (ret)
- goto err_gpio;
-
- udelay(1); /* > 150 ns */
- gpio_set_value(pdata->gpio_npdn, 1);
- }
- }
-
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- goto err_register;
+ return ret;
}
/* power on device */
ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
-
-err_register:
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 0);
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
-err_gpio:
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_free(pdata->gpio_power);
-err_out:
- return ret;
}
static int ak4641_remove(struct snd_soc_codec *codec)
{
- struct ak4641_platform_data *pdata = codec->dev->platform_data;
-
ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_power);
- }
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
return 0;
}
@@ -604,6 +560,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
int ret;
@@ -612,16 +569,62 @@ static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
if (!ak4641)
return -ENOMEM;
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
i2c_set_clientdata(i2c, ak4641);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret != 0)
+ goto err_gpio2;
+
+ return 0;
+
+err_gpio2:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
return ret;
}
static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
{
+ struct ak4641_platform_data *pdata = i2c->dev.platform_data;
+
snd_soc_unregister_codec(&i2c->dev);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+
return 0;
}
@@ -641,23 +644,7 @@ static struct i2c_driver ak4641_i2c_driver = {
.id_table = ak4641_i2c_id,
};
-static int __init ak4641_modinit(void)
-{
- int ret;
-
- ret = i2c_add_driver(&ak4641_i2c_driver);
- if (ret != 0)
- pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
-
- return ret;
-}
-module_init(ak4641_modinit);
-
-static void __exit ak4641_exit(void)
-{
- i2c_del_driver(&ak4641_i2c_driver);
-}
-module_exit(ak4641_exit);
+module_i2c_driver(ak4641_i2c_driver);
MODULE_DESCRIPTION("SoC AK4641 driver");
MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index d47b62ddb21..1960478ce6b 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -705,8 +705,7 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int alc5623_pcm_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 snd_soc_codec *codec = dai->codec;
struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
int coeff, rate;
u16 iface;
@@ -1084,25 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = {
.id_table = alc5623_i2c_table,
};
-static int __init alc5623_modinit(void)
-{
- int ret;
-
- ret = i2c_add_driver(&alc5623_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "%s: can't add i2c driver", __func__);
- return ret;
- }
-
- return ret;
-}
-module_init(alc5623_modinit);
-
-static void __exit alc5623_modexit(void)
-{
- i2c_del_driver(&alc5623_i2c_driver);
-}
-module_exit(alc5623_modexit);
+module_i2c_driver(alc5623_i2c_driver);
MODULE_DESCRIPTION("ASoC alc5621/2/3 driver");
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index e2111e0ccad..7dd02420b36 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -861,8 +861,7 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int alc5632_pcm_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 snd_soc_codec *codec = dai->codec;
int coeff, rate;
u16 iface;
@@ -1131,7 +1130,7 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, alc5632);
- alc5632->regmap = regmap_init_i2c(client, &alc5632_regmap);
+ alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap);
if (IS_ERR(alc5632->regmap)) {
ret = PTR_ERR(alc5632->regmap);
dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
@@ -1143,7 +1142,6 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client,
if (ret1 != 0 || ret2 != 0) {
dev_err(&client->dev,
"Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2);
- regmap_exit(alc5632->regmap);
return -EIO;
}
@@ -1152,14 +1150,12 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client,
if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
- regmap_exit(alc5632->regmap);
return -EINVAL;
}
ret = alc5632_reset(alc5632->regmap);
if (ret < 0) {
dev_err(&client->dev, "Failed to issue reset\n");
- regmap_exit(alc5632->regmap);
return ret;
}
@@ -1177,7 +1173,6 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client,
if (ret < 0) {
dev_err(&client->dev, "Failed to register codec: %d\n", ret);
- regmap_exit(alc5632->regmap);
return ret;
}
@@ -1186,9 +1181,7 @@ static __devinit int alc5632_i2c_probe(struct i2c_client *client,
static __devexit int alc5632_i2c_remove(struct i2c_client *client)
{
- struct alc5632_priv *alc5632 = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev);
- regmap_exit(alc5632->regmap);
return 0;
}
@@ -1209,25 +1202,7 @@ static struct i2c_driver alc5632_i2c_driver = {
.id_table = alc5632_i2c_table,
};
-static int __init alc5632_modinit(void)
-{
- int ret;
-
- ret = i2c_add_driver(&alc5632_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "%s: can't add i2c driver", __func__);
- return ret;
- }
-
- return ret;
-}
-module_init(alc5632_modinit);
-
-static void __exit alc5632_modexit(void)
-{
- i2c_del_driver(&alc5632_i2c_driver);
-}
-module_exit(alc5632_modexit);
+module_i2c_driver(alc5632_i2c_driver);
MODULE_DESCRIPTION("ASoC ALC5632 driver");
MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 1d672f52866..047917f0b8a 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -307,8 +307,7 @@ static int cs4270_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 snd_soc_codec *codec = dai->codec;
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
@@ -600,10 +599,12 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec)
static int cs4270_soc_resume(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
- int reg;
+ int reg, ret;
- regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret != 0)
+ return ret;
/* In case the device was put to hard reset during sleep, we need to
* wait 500ns here before any I2C communication. */
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index bf7141280a7..9eb01d7d58a 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -318,8 +318,7 @@ static int cs4271_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 snd_soc_codec *codec = dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int i, ret;
unsigned int ratio, val;
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index a8bf588e874..091d0193f50 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -141,15 +141,15 @@ static const struct soc_enum cs42l51_chan_mix =
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
- 7, 0xffffff99, 0x18, adc_pcm_tlv),
+ 6, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("PCM Playback Switch",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
- 8, 0xffffff19, 0x18, aout_tlv),
+ 0, 0x34, 0xE4, aout_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
- 7, 0xffffff99, 0x18, adc_pcm_tlv),
+ 6, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
@@ -356,8 +356,7 @@ static int cs42l51_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 snd_soc_codec *codec = dai->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
new file mode 100644
index 00000000000..a7109413aef
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.c
@@ -0,0 +1,1295 @@
+/*
+ * cs42l52.c -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.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/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs42l52.h>
+#include "cs42l52.h"
+
+struct sp_config {
+ u8 spc, format, spfs;
+ u32 srate;
+};
+
+struct cs42l52_private {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct device *dev;
+ struct sp_config config;
+ struct cs42l52_platform_data pdata;
+ u32 sysclk;
+ u8 mclksel;
+ u32 mclk;
+ u8 flags;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+#endif
+};
+
+static const struct reg_default cs42l52_reg_defaults[] = {
+ { CS42L52_PWRCTL1, 0x9F }, /* r02 PWRCTL 1 */
+ { CS42L52_PWRCTL2, 0x07 }, /* r03 PWRCTL 2 */
+ { CS42L52_PWRCTL3, 0xFF }, /* r04 PWRCTL 3 */
+ { CS42L52_CLK_CTL, 0xA0 }, /* r05 Clocking Ctl */
+ { CS42L52_IFACE_CTL1, 0x00 }, /* r06 Interface Ctl 1 */
+ { CS42L52_ADC_PGA_A, 0x80 }, /* r08 Input A Select */
+ { CS42L52_ADC_PGA_B, 0x80 }, /* r09 Input B Select */
+ { CS42L52_ANALOG_HPF_CTL, 0xA5 }, /* r0A Analog HPF Ctl */
+ { CS42L52_ADC_HPF_FREQ, 0x00 }, /* r0B ADC HPF Corner Freq */
+ { CS42L52_ADC_MISC_CTL, 0x00 }, /* r0C Misc. ADC Ctl */
+ { CS42L52_PB_CTL1, 0x60 }, /* r0D Playback Ctl 1 */
+ { CS42L52_MISC_CTL, 0x02 }, /* r0E Misc. Ctl */
+ { CS42L52_PB_CTL2, 0x00 }, /* r0F Playback Ctl 2 */
+ { CS42L52_MICA_CTL, 0x00 }, /* r10 MICA Amp Ctl */
+ { CS42L52_MICB_CTL, 0x00 }, /* r11 MICB Amp Ctl */
+ { CS42L52_PGAA_CTL, 0x00 }, /* r12 PGAA Vol, Misc. */
+ { CS42L52_PGAB_CTL, 0x00 }, /* r13 PGAB Vol, Misc. */
+ { CS42L52_PASSTHRUA_VOL, 0x00 }, /* r14 Bypass A Vol */
+ { CS42L52_PASSTHRUB_VOL, 0x00 }, /* r15 Bypass B Vol */
+ { CS42L52_ADCA_VOL, 0x00 }, /* r16 ADCA Volume */
+ { CS42L52_ADCB_VOL, 0x00 }, /* r17 ADCB Volume */
+ { CS42L52_ADCA_MIXER_VOL, 0x80 }, /* r18 ADCA Mixer Volume */
+ { CS42L52_ADCB_MIXER_VOL, 0x80 }, /* r19 ADCB Mixer Volume */
+ { CS42L52_PCMA_MIXER_VOL, 0x00 }, /* r1A PCMA Mixer Volume */
+ { CS42L52_PCMB_MIXER_VOL, 0x00 }, /* r1B PCMB Mixer Volume */
+ { CS42L52_BEEP_FREQ, 0x00 }, /* r1C Beep Freq on Time */
+ { CS42L52_BEEP_VOL, 0x00 }, /* r1D Beep Volume off Time */
+ { CS42L52_BEEP_TONE_CTL, 0x00 }, /* r1E Beep Tone Cfg. */
+ { CS42L52_TONE_CTL, 0x00 }, /* r1F Tone Ctl */
+ { CS42L52_MASTERA_VOL, 0x88 }, /* r20 Master A Volume */
+ { CS42L52_MASTERB_VOL, 0x00 }, /* r21 Master B Volume */
+ { CS42L52_HPA_VOL, 0x00 }, /* r22 Headphone A Volume */
+ { CS42L52_HPB_VOL, 0x00 }, /* r23 Headphone B Volume */
+ { CS42L52_SPKA_VOL, 0x00 }, /* r24 Speaker A Volume */
+ { CS42L52_SPKB_VOL, 0x00 }, /* r25 Speaker B Volume */
+ { CS42L52_ADC_PCM_MIXER, 0x00 }, /* r26 Channel Mixer and Swap */
+ { CS42L52_LIMITER_CTL1, 0x00 }, /* r27 Limit Ctl 1 Thresholds */
+ { CS42L52_LIMITER_CTL2, 0x7F }, /* r28 Limit Ctl 2 Release Rate */
+ { CS42L52_LIMITER_AT_RATE, 0xC0 }, /* r29 Limiter Attack Rate */
+ { CS42L52_ALC_CTL, 0x00 }, /* r2A ALC Ctl 1 Attack Rate */
+ { CS42L52_ALC_RATE, 0x3F }, /* r2B ALC Release Rate */
+ { CS42L52_ALC_THRESHOLD, 0x3f }, /* r2C ALC Thresholds */
+ { CS42L52_NOISE_GATE_CTL, 0x00 }, /* r2D Noise Gate Ctl */
+ { CS42L52_CLK_STATUS, 0x00 }, /* r2E Overflow and Clock Status */
+ { CS42L52_BATT_COMPEN, 0x00 }, /* r2F battery Compensation */
+ { CS42L52_BATT_LEVEL, 0x00 }, /* r30 VP Battery Level */
+ { CS42L52_SPK_STATUS, 0x00 }, /* r31 Speaker Status */
+ { CS42L52_TEM_CTL, 0x3B }, /* r32 Temp Ctl */
+ { CS42L52_THE_FOLDBACK, 0x00 }, /* r33 Foldback */
+};
+
+static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_CHIP:
+ case CS42L52_PWRCTL1:
+ case CS42L52_PWRCTL2:
+ case CS42L52_PWRCTL3:
+ case CS42L52_CLK_CTL:
+ case CS42L52_IFACE_CTL1:
+ case CS42L52_IFACE_CTL2:
+ case CS42L52_ADC_PGA_A:
+ case CS42L52_ADC_PGA_B:
+ case CS42L52_ANALOG_HPF_CTL:
+ case CS42L52_ADC_HPF_FREQ:
+ case CS42L52_ADC_MISC_CTL:
+ case CS42L52_PB_CTL1:
+ case CS42L52_MISC_CTL:
+ case CS42L52_PB_CTL2:
+ case CS42L52_MICA_CTL:
+ case CS42L52_MICB_CTL:
+ case CS42L52_PGAA_CTL:
+ case CS42L52_PGAB_CTL:
+ case CS42L52_PASSTHRUA_VOL:
+ case CS42L52_PASSTHRUB_VOL:
+ case CS42L52_ADCA_VOL:
+ case CS42L52_ADCB_VOL:
+ case CS42L52_ADCA_MIXER_VOL:
+ case CS42L52_ADCB_MIXER_VOL:
+ case CS42L52_PCMA_MIXER_VOL:
+ case CS42L52_PCMB_MIXER_VOL:
+ case CS42L52_BEEP_FREQ:
+ case CS42L52_BEEP_VOL:
+ case CS42L52_BEEP_TONE_CTL:
+ case CS42L52_TONE_CTL:
+ case CS42L52_MASTERA_VOL:
+ case CS42L52_MASTERB_VOL:
+ case CS42L52_HPA_VOL:
+ case CS42L52_HPB_VOL:
+ case CS42L52_SPKA_VOL:
+ case CS42L52_SPKB_VOL:
+ case CS42L52_ADC_PCM_MIXER:
+ case CS42L52_LIMITER_CTL1:
+ case CS42L52_LIMITER_CTL2:
+ case CS42L52_LIMITER_AT_RATE:
+ case CS42L52_ALC_CTL:
+ case CS42L52_ALC_RATE:
+ case CS42L52_ALC_THRESHOLD:
+ case CS42L52_NOISE_GATE_CTL:
+ case CS42L52_CLK_STATUS:
+ case CS42L52_BATT_COMPEN:
+ case CS42L52_BATT_LEVEL:
+ case CS42L52_SPK_STATUS:
+ case CS42L52_TEM_CTL:
+ case CS42L52_THE_FOLDBACK:
+ case CS42L52_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_IFACE_CTL2:
+ case CS42L52_CLK_STATUS:
+ case CS42L52_BATT_LEVEL:
+ case CS42L52_SPK_STATUS:
+ case CS42L52_CHARGE_PUMP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static const unsigned int limiter_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const char * const cs42l52_adca_text[] = {
+ "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
+
+static const char * const cs42l52_adcb_text[] = {
+ "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"};
+
+static const struct soc_enum adca_enum =
+ SOC_ENUM_SINGLE(CS42L52_ADC_PGA_A, 5,
+ ARRAY_SIZE(cs42l52_adca_text), cs42l52_adca_text);
+
+static const struct soc_enum adcb_enum =
+ SOC_ENUM_SINGLE(CS42L52_ADC_PGA_B, 5,
+ ARRAY_SIZE(cs42l52_adcb_text), cs42l52_adcb_text);
+
+static const struct snd_kcontrol_new adca_mux =
+ SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum);
+
+static const struct snd_kcontrol_new adcb_mux =
+ SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum);
+
+static const char * const mic_bias_level_text[] = {
+ "0.5 +VA", "0.6 +VA", "0.7 +VA",
+ "0.8 +VA", "0.83 +VA", "0.91 +VA"
+};
+
+static const struct soc_enum mic_bias_level_enum =
+ SOC_ENUM_SINGLE(CS42L52_IFACE_CTL1, 0,
+ ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text);
+
+static const char * const cs42l52_mic_text[] = { "Single", "Differential" };
+
+static const struct soc_enum mica_enum =
+ SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5,
+ ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+
+static const struct soc_enum micb_enum =
+ SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5,
+ ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+
+static const struct snd_kcontrol_new mica_mux =
+ SOC_DAPM_ENUM("Left Mic Input Capture Mux", mica_enum);
+
+static const struct snd_kcontrol_new micb_mux =
+ SOC_DAPM_ENUM("Right Mic Input Capture Mux", micb_enum);
+
+static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
+
+static const struct soc_enum digital_output_mux_enum =
+ SOC_ENUM_SINGLE(CS42L52_ADC_MISC_CTL, 6,
+ ARRAY_SIZE(digital_output_mux_text),
+ digital_output_mux_text);
+
+static const struct snd_kcontrol_new digital_output_mux =
+ SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum);
+
+static const char * const hp_gain_num_text[] = {
+ "0.3959", "0.4571", "0.5111", "0.6047",
+ "0.7099", "0.8399", "1.000", "1.1430"
+};
+
+static const struct soc_enum hp_gain_enum =
+ SOC_ENUM_SINGLE(CS42L52_PB_CTL1, 4,
+ ARRAY_SIZE(hp_gain_num_text), hp_gain_num_text);
+
+static const char * const beep_pitch_text[] = {
+ "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+ "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static const struct soc_enum beep_pitch_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 4,
+ ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+ "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+ "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+ "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static const struct soc_enum beep_ontime_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 0,
+ ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+ "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+ "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static const struct soc_enum beep_offtime_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_VOL, 5,
+ ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+
+static const char * const beep_config_text[] = {
+ "Off", "Single", "Multiple", "Continuous"
+};
+
+static const struct soc_enum beep_config_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 6,
+ ARRAY_SIZE(beep_config_text), beep_config_text);
+
+static const char * const beep_bass_text[] = {
+ "50 Hz", "100 Hz", "200 Hz", "250 Hz"
+};
+
+static const struct soc_enum beep_bass_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 1,
+ ARRAY_SIZE(beep_bass_text), beep_bass_text);
+
+static const char * const beep_treble_text[] = {
+ "5 kHz", "7 kHz", "10 kHz", " 15 kHz"
+};
+
+static const struct soc_enum beep_treble_enum =
+ SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 3,
+ ARRAY_SIZE(beep_treble_text), beep_treble_text);
+
+static const char * const ng_threshold_text[] = {
+ "-34dB", "-37dB", "-40dB", "-43dB",
+ "-46dB", "-52dB", "-58dB", "-64dB"
+};
+
+static const struct soc_enum ng_threshold_enum =
+ SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 2,
+ ARRAY_SIZE(ng_threshold_text), ng_threshold_text);
+
+static const char * const cs42l52_ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms"};
+
+static const struct soc_enum ng_delay_enum =
+ SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 0,
+ ARRAY_SIZE(cs42l52_ng_delay_text), cs42l52_ng_delay_text);
+
+static const char * const cs42l52_ng_type_text[] = {
+ "Apply Specific", "Apply All"
+};
+
+static const struct soc_enum ng_type_enum =
+ SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 6,
+ ARRAY_SIZE(cs42l52_ng_type_text), cs42l52_ng_type_text);
+
+static const char * const left_swap_text[] = {
+ "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+ "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 1,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adca_mixer =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 1,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcma_mixer =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 1,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adcb_mixer =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 1,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcmb_mixer =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+
+static const struct snd_kcontrol_new passthrul_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new passthrur_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0);
+
+static const struct snd_kcontrol_new spkl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1);
+
+static const struct snd_kcontrol_new spkr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new hpl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new hpr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1);
+
+static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL,
+ CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
+ CS42L52_HPB_VOL, 0, 0x34, 0xCC, hpd_tlv),
+
+ SOC_ENUM("Headphone Analog Gain", hp_gain_enum),
+
+ SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL,
+ CS42L52_SPKB_VOL, 7, 0x1, 0xff, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
+ CS42L52_PASSTHRUB_VOL, 6, 0x18, 0x90, pga_tlv),
+
+ SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
+
+ SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL,
+ CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv),
+
+ SOC_ENUM("MIC Bias Level", mic_bias_level_enum),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL,
+ CS42L52_ADCB_VOL, 7, 0x80, 0xA0, ipd_tlv),
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+ CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
+ 6, 0x7f, 0x19, ipd_tlv),
+
+ SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL,
+ CS42L52_ADCB_MIXER_VOL, 7, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 0, 0x28, 0x30, pga_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL,
+ 6, 0x7f, 0x19, hl_tlv),
+ SOC_DOUBLE_R("PCM Mixer Switch",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1),
+
+ SOC_ENUM("Beep Config", beep_config_enum),
+ SOC_ENUM("Beep Pitch", beep_pitch_enum),
+ SOC_ENUM("Beep on Time", beep_ontime_enum),
+ SOC_ENUM("Beep off Time", beep_offtime_enum),
+ SOC_SINGLE_TLV("Beep Volume", CS42L52_BEEP_VOL, 0, 0x1f, 0x07, hl_tlv),
+ SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1),
+ SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+ SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+ SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1),
+ SOC_SINGLE_TLV("Treble Gain Volume",
+ CS42L52_TONE_CTL, 4, 15, 1, hl_tlv),
+ SOC_SINGLE_TLV("Bass Gain Volume",
+ CS42L52_TONE_CTL, 0, 15, 1, hl_tlv),
+
+ /* Limiter */
+ SOC_SINGLE_TLV("Limiter Max Threshold Volume",
+ CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Cushion Threshold Volume",
+ CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Release Rate Volume",
+ CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Attack Rate Volume",
+ CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv),
+
+ SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0),
+ SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0),
+ SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0),
+
+ /* ALC */
+ SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 2, 7, 0, limiter_tlv),
+
+ SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 7, 1, 1),
+ SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 6, 1, 1),
+ SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0),
+
+ /* Noise gate */
+ SOC_ENUM("NG Type Switch", ng_type_enum),
+ SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0),
+ SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1),
+ SOC_ENUM("NG Threshold", ng_threshold_enum),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0),
+
+ SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1),
+ SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1),
+ SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0),
+ SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0),
+
+ SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0),
+ SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0),
+ SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0),
+
+ SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0),
+ SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0),
+ SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0),
+ SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0),
+
+ SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0),
+ SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0),
+
+ SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0),
+ SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0),
+
+ SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0),
+ SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0),
+
+};
+
+static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("MICA"),
+ SND_SOC_DAPM_INPUT("MICB"),
+ SND_SOC_DAPM_SIGGEN("Beep"),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("MICA Mux", SND_SOC_NOPM, 0, 0, &mica_mux),
+ SND_SOC_DAPM_MUX("MICB Mux", SND_SOC_NOPM, 0, 0, &micb_mux),
+
+ SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1),
+ SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux),
+ SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux),
+
+ SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM,
+ 0, 0, &adca_mixer),
+ SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM,
+ 0, 0, &adcb_mixer),
+
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM,
+ 0, 0, &digital_output_mux),
+
+ SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFINL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL,
+ 6, 0, &passthrul_ctl),
+ SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL,
+ 7, 0, &passthrur_ctl),
+
+ SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM,
+ 0, 0, &pcma_mixer),
+ SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM,
+ 0, 0, &pcmb_mixer),
+
+ SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl),
+ SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl),
+
+ SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl),
+ SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTA"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTB"),
+
+};
+
+static const struct snd_soc_dapm_route cs42l52_audio_map[] = {
+
+ {"Capture", NULL, "AIFOUTL"},
+ {"Capture", NULL, "AIFOUTL"},
+
+ {"AIFOUTL", NULL, "Output Mux"},
+ {"AIFOUTR", NULL, "Output Mux"},
+
+ {"Output Mux", "ADC", "ADC Left"},
+ {"Output Mux", "ADC", "ADC Right"},
+
+ {"ADC Left", NULL, "Charge Pump"},
+ {"ADC Right", NULL, "Charge Pump"},
+
+ {"Charge Pump", NULL, "ADC Left Mux"},
+ {"Charge Pump", NULL, "ADC Right Mux"},
+
+ {"ADC Left Mux", "Input1A", "AIN1L"},
+ {"ADC Right Mux", "Input1B", "AIN1R"},
+ {"ADC Left Mux", "Input2A", "AIN2L"},
+ {"ADC Right Mux", "Input2B", "AIN2R"},
+ {"ADC Left Mux", "Input3A", "AIN3L"},
+ {"ADC Right Mux", "Input3B", "AIN3R"},
+ {"ADC Left Mux", "Input4A", "AIN4L"},
+ {"ADC Right Mux", "Input4B", "AIN4R"},
+ {"ADC Left Mux", "PGA Input Left", "PGA Left"},
+ {"ADC Right Mux", "PGA Input Right" , "PGA Right"},
+
+ {"PGA Left", "Switch", "AIN1L"},
+ {"PGA Right", "Switch", "AIN1R"},
+ {"PGA Left", "Switch", "AIN2L"},
+ {"PGA Right", "Switch", "AIN2R"},
+ {"PGA Left", "Switch", "AIN3L"},
+ {"PGA Right", "Switch", "AIN3R"},
+ {"PGA Left", "Switch", "AIN4L"},
+ {"PGA Right", "Switch", "AIN4R"},
+
+ {"PGA Left", "Switch", "PGA MICA"},
+ {"PGA MICA", NULL, "MICA"},
+
+ {"PGA Right", "Switch", "PGA MICB"},
+ {"PGA MICB", NULL, "MICB"},
+
+ {"HPOUTA", NULL, "HP Left Amp"},
+ {"HPOUTB", NULL, "HP Right Amp"},
+ {"HP Left Amp", NULL, "Bypass Left"},
+ {"HP Right Amp", NULL, "Bypass Right"},
+ {"Bypass Left", "Switch", "PGA Left"},
+ {"Bypass Right", "Switch", "PGA Right"},
+ {"HP Left Amp", "Switch", "DAC Left"},
+ {"HP Right Amp", "Switch", "DAC Right"},
+
+ {"SPKOUTA", NULL, "SPK Left Amp"},
+ {"SPKOUTB", NULL, "SPK Right Amp"},
+
+ {"SPK Left Amp", NULL, "Beep"},
+ {"SPK Right Amp", NULL, "Beep"},
+ {"SPK Left Amp", "Switch", "Playback"},
+ {"SPK Right Amp", "Switch", "Playback"},
+
+ {"DAC Left", NULL, "Beep"},
+ {"DAC Right", NULL, "Beep"},
+ {"DAC Left", NULL, "Playback"},
+ {"DAC Right", NULL, "Playback"},
+
+ {"Output Mux", "DSP", "Playback"},
+ {"Output Mux", "DSP", "Playback"},
+
+ {"AIFINL", NULL, "Playback"},
+ {"AIFINR", NULL, "Playback"},
+
+};
+
+struct cs42l52_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 speed;
+ u8 group;
+ u8 videoclk;
+ u8 ratio;
+ u8 mclkdiv2;
+};
+
+static const struct cs42l52_clk_para clk_map_table[] = {
+ /*8k*/
+ {12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /*11.025k*/
+ {11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /*16k*/
+ {12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /*22.05k*/
+ {11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 32k */
+ {12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /* 44.1k */
+ {11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 48k */
+ {12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /* 88.2k */
+ {11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 96k */
+ {12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+};
+
+static int cs42l52_get_clk(int mclk, int rate)
+{
+ int i, ret = 0;
+ u_int mclk1, mclk2 = 0;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate) {
+ mclk1 = clk_map_table[i].mclk;
+ if (abs(mclk - mclk1) < abs(mclk - mclk2)) {
+ mclk2 = mclk1;
+ ret = i;
+ }
+ }
+ }
+ if (ret > ARRAY_SIZE(clk_map_table))
+ return -EINVAL;
+ return ret;
+}
+
+static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) {
+ cs42l52->sysclk = freq;
+ } else {
+ dev_err(codec->dev, "Invalid freq paramter\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = CS42L52_IFACE_CTL1_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface = CS42L52_IFACE_CTL1_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S |
+ CS42L52_IFACE_CTL1_DAC_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J |
+ CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ cs42l52->config.format = iface;
+ snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+
+ return 0;
+}
+
+static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute)
+ snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_MUTE);
+ else
+ snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_UNMUTE);
+
+ return 0;
+}
+
+static int cs42l52_pcm_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 cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ u32 clk = 0;
+ int index;
+
+ index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params));
+ if (index >= 0) {
+ cs42l52->sysclk = clk_map_table[index].mclk;
+
+ clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) |
+ (clk_map_table[index].group << CLK_32K_SR_SHIFT) |
+ (clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) |
+ (clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
+ clk_map_table[index].mclkdiv2;
+
+ snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+ } else {
+ dev_err(codec->dev, "can't get correct mclk\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, CS42L52_PWRCTL1,
+ CS42L52_PWRCTL1_PDN_CODEC, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l52->regmap, false);
+ regcache_sync(cs42l52->regmap);
+ }
+ snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ regcache_cache_only(cs42l52->regmap, true);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static struct snd_soc_dai_ops cs42l52_ops = {
+ .hw_params = cs42l52_pcm_hw_params,
+ .digital_mute = cs42l52_digital_mute,
+ .set_fmt = cs42l52_set_fmt,
+ .set_sysclk = cs42l52_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs42l52_dai = {
+ .name = "cs42l52",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .ops = &cs42l52_ops,
+};
+
+static int cs42l52_suspend(struct snd_soc_codec *codec)
+{
+ cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int cs42l52_resume(struct snd_soc_codec *codec)
+{
+ cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int beep_rates[] = {
+ 261, 522, 585, 667, 706, 774, 889, 1000,
+ 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l52_beep_work(struct work_struct *work)
+{
+ struct cs42l52_private *cs42l52 =
+ container_of(work, struct cs42l52_private, beep_work);
+ struct snd_soc_codec *codec = cs42l52->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i;
+ int val = 0;
+ int best = 0;
+
+ if (cs42l52->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_rates); i++) {
+ if (abs(cs42l52->beep_rate - beep_rates[i]) <
+ abs(cs42l52->beep_rate - beep_rates[best]))
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ beep_rates[best], cs42l52->beep_rate);
+
+ val = (best << CS42L52_BEEP_RATE_SHIFT);
+
+ snd_soc_dapm_enable_pin(dapm, "Beep");
+ } else {
+ dev_dbg(codec->dev, "Disabling beep\n");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+ CS42L52_BEEP_RATE_MASK, val);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_codec *codec = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 261;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ cs42l52->beep_rate = hz;
+ schedule_work(&cs42l52->beep_work);
+ return 0;
+}
+
+static ssize_t cs42l52_beep_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(cs42l52->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+
+static void cs42l52_init_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ cs42l52->beep = input_allocate_device();
+ if (!cs42l52->beep) {
+ dev_err(codec->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work);
+ cs42l52->beep_rate = 0;
+
+ cs42l52->beep->name = "CS42L52 Beep Generator";
+ cs42l52->beep->phys = dev_name(codec->dev);
+ cs42l52->beep->id.bustype = BUS_I2C;
+
+ cs42l52->beep->evbit[0] = BIT_MASK(EV_SND);
+ cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ cs42l52->beep->event = cs42l52_beep_event;
+ cs42l52->beep->dev.parent = codec->dev;
+ input_set_drvdata(cs42l52->beep, codec);
+
+ ret = input_register_device(cs42l52->beep);
+ if (ret != 0) {
+ input_free_device(cs42l52->beep);
+ cs42l52->beep = NULL;
+ dev_err(codec->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(codec->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void cs42l52_free_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ device_remove_file(codec->dev, &dev_attr_beep);
+ input_unregister_device(cs42l52->beep);
+ cancel_work_sync(&cs42l52->beep_work);
+ cs42l52->beep = NULL;
+
+ snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+ CS42L52_BEEP_EN_MASK, 0);
+}
+#else
+static void cs42l52_init_beep(struct snd_soc_codec *codec)
+{
+}
+
+static void cs42l52_free_beep(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+static int cs42l52_probe(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = cs42l52->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+ regcache_cache_only(cs42l52->regmap, true);
+
+ cs42l52_init_beep(codec);
+
+ cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ cs42l52->sysclk = CS42L52_DEFAULT_CLK;
+ cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
+
+ /* Set Platform MICx CFG */
+ snd_soc_update_bits(codec, CS42L52_MICA_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.mica_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ snd_soc_update_bits(codec, CS42L52_MICB_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.micb_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ /* if Single Ended, Get Mic_Select */
+ if (cs42l52->pdata.mica_cfg)
+ snd_soc_update_bits(codec, CS42L52_MICA_CTL,
+ CS42L52_MIC_CTL_MIC_SEL_MASK,
+ cs42l52->pdata.mica_sel <<
+ CS42L52_MIC_CTL_MIC_SEL_SHIFT);
+ if (cs42l52->pdata.micb_cfg)
+ snd_soc_update_bits(codec, CS42L52_MICB_CTL,
+ CS42L52_MIC_CTL_MIC_SEL_MASK,
+ cs42l52->pdata.micb_sel <<
+ CS42L52_MIC_CTL_MIC_SEL_SHIFT);
+
+ /* Set Platform Charge Pump Freq */
+ snd_soc_update_bits(codec, CS42L52_CHARGE_PUMP,
+ CS42L52_CHARGE_PUMP_MASK,
+ cs42l52->pdata.chgfreq <<
+ CS42L52_CHARGE_PUMP_SHIFT);
+
+ /* Set Platform Bias Level */
+ snd_soc_update_bits(codec, CS42L52_IFACE_CTL2,
+ CS42L52_IFACE_CTL2_BIAS_LVL,
+ cs42l52->pdata.micbias_lvl);
+
+ return ret;
+}
+
+static int cs42l52_remove(struct snd_soc_codec *codec)
+{
+ cs42l52_free_beep(codec);
+ cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
+ .probe = cs42l52_probe,
+ .remove = cs42l52_remove,
+ .suspend = cs42l52_suspend,
+ .resume = cs42l52_resume,
+ .set_bias_level = cs42l52_set_bias_level,
+
+ .dapm_widgets = cs42l52_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
+ .dapm_routes = cs42l52_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map),
+
+ .controls = cs42l52_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+};
+
+/* Current and threshold powerup sequence Pg37 */
+static const struct reg_default cs42l52_threshold_patch[] = {
+
+ { 0x00, 0x99 },
+ { 0x3E, 0xBA },
+ { 0x47, 0x80 },
+ { 0x32, 0xBB },
+ { 0x32, 0x3B },
+ { 0x00, 0x00 },
+
+};
+
+static struct regmap_config cs42l52_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L52_MAX_REGISTER,
+ .reg_defaults = cs42l52_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
+ .readable_reg = cs42l52_readable_register,
+ .volatile_reg = cs42l52_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs42l52_private *cs42l52;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
+ GFP_KERNEL);
+ if (cs42l52 == NULL)
+ return -ENOMEM;
+ cs42l52->dev = &i2c_client->dev;
+
+ cs42l52->regmap = regmap_init_i2c(i2c_client, &cs42l52_regmap);
+ if (IS_ERR(cs42l52->regmap)) {
+ ret = PTR_ERR(cs42l52->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ goto err;
+ }
+
+ i2c_set_clientdata(i2c_client, cs42l52);
+
+ if (dev_get_platdata(&i2c_client->dev))
+ memcpy(&cs42l52->pdata, dev_get_platdata(&i2c_client->dev),
+ sizeof(cs42l52->pdata));
+
+ ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch,
+ ARRAY_SIZE(cs42l52_threshold_patch));
+ if (ret != 0)
+ dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n",
+ ret);
+
+ ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ devid = reg & CS42L52_CHIP_ID_MASK;
+ if (devid != CS42L52_CHIP_ID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L52 Device ID (%X). Expected %X\n",
+ devid, CS42L52_CHIP_ID);
+ goto err_regmap;
+ }
+
+ regcache_cache_only(cs42l52->regmap, true);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs42l52, &cs42l52_dai, 1);
+ if (ret < 0)
+ goto err_regmap;
+ return 0;
+
+err_regmap:
+ regmap_exit(cs42l52->regmap);
+
+err:
+ return ret;
+}
+
+static int cs42l52_i2c_remove(struct i2c_client *client)
+{
+ struct cs42l52_private *cs42l52 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ regmap_exit(cs42l52->regmap);
+
+ return 0;
+}
+
+static const struct i2c_device_id cs42l52_id[] = {
+ { "cs42l52", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l52_id);
+
+static struct i2c_driver cs42l52_i2c_driver = {
+ .driver = {
+ .name = "cs42l52",
+ .owner = THIS_MODULE,
+ },
+ .id_table = cs42l52_id,
+ .probe = cs42l52_i2c_probe,
+ .remove = __devexit_p(cs42l52_i2c_remove),
+};
+
+module_i2c_driver(cs42l52_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L52 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
new file mode 100644
index 00000000000..60985c05907
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.h
@@ -0,0 +1,274 @@
+/*
+ * cs42l52.h -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.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 __CS42L52_H__
+#define __CS42L52_H__
+
+#define CS42L52_NAME "CS42L52"
+#define CS42L52_DEFAULT_CLK 12000000
+#define CS42L52_MIN_CLK 11000000
+#define CS42L52_MAX_CLK 27000000
+#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE
+#define CS42L52_DEFAULT_MAX_CHANS 2
+#define CS42L52_SYSCLK 1
+
+#define CS42L52_CHIP_SWICTH (1 << 17)
+#define CS42L52_ALL_IN_ONE (1 << 16)
+#define CS42L52_CHIP_ONE 0x00
+#define CS42L52_CHIP_TWO 0x01
+#define CS42L52_CHIP_THR 0x02
+#define CS42L52_CHIP_MASK 0x0f
+
+#define CS42L52_FIX_BITS_CTL 0x00
+#define CS42L52_CHIP 0x01
+#define CS42L52_CHIP_ID 0xE0
+#define CS42L52_CHIP_ID_MASK 0xF8
+#define CS42L52_CHIP_REV_A0 0x00
+#define CS42L52_CHIP_REV_A1 0x01
+#define CS42L52_CHIP_REV_B0 0x02
+#define CS42L52_CHIP_REV_MASK 0x03
+
+#define CS42L52_PWRCTL1 0x02
+#define CS42L52_PWRCTL1_PDN_ALL 0x9F
+#define CS42L52_PWRCTL1_PDN_CHRG 0x80
+#define CS42L52_PWRCTL1_PDN_PGAB 0x10
+#define CS42L52_PWRCTL1_PDN_PGAA 0x08
+#define CS42L52_PWRCTL1_PDN_ADCB 0x04
+#define CS42L52_PWRCTL1_PDN_ADCA 0x02
+#define CS42L52_PWRCTL1_PDN_CODEC 0x01
+
+#define CS42L52_PWRCTL2 0x03
+#define CS42L52_PWRCTL2_OVRDB (1 << 4)
+#define CS42L52_PWRCTL2_OVRDA (1 << 3)
+#define CS42L52_PWRCTL2_PDN_MICB (1 << 2)
+#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2
+#define CS42L52_PWRCTL2_PDN_MICA (1 << 1)
+#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1
+#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0)
+#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0
+
+#define CS42L52_PWRCTL3 0x04
+#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6
+#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4
+#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2
+#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2)
+#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0)
+#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0
+#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02
+
+#define CS42L52_DEFAULT_OUTPUT_STATE 0x05
+#define CS42L52_PWRCTL3_CONF_MASK 0x03
+
+#define CS42L52_CLK_CTL 0x05
+#define CLK_AUTODECT_ENABLE (1 << 7)
+#define CLK_SPEED_SHIFT 5
+#define CLK_DS_MODE 0x00
+#define CLK_SS_MODE 0x01
+#define CLK_HS_MODE 0x02
+#define CLK_QS_MODE 0x03
+#define CLK_32K_SR_SHIFT 4
+#define CLK_32K 0x01
+#define CLK_NO_32K 0x00
+#define CLK_27M_MCLK_SHIFT 3
+#define CLK_27M_MCLK 0x01
+#define CLK_NO_27M 0x00
+#define CLK_RATIO_SHIFT 1
+#define CLK_R_128 0x00
+#define CLK_R_125 0x01
+#define CLK_R_132 0x02
+#define CLK_R_136 0x03
+
+#define CS42L52_IFACE_CTL1 0x06
+#define CS42L52_IFACE_CTL1_MASTER (1 << 7)
+#define CS42L52_IFACE_CTL1_SLAVE (0 << 7)
+#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6)
+#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5)
+#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5)
+#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4)
+#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2)
+#define CS42L52_IFACE_CTL1_WL_32BIT (0x00)
+#define CS42L52_IFACE_CTL1_WL_24BIT (0x01)
+#define CS42L52_IFACE_CTL1_WL_20BIT (0x02)
+#define CS42L52_IFACE_CTL1_WL_16BIT (0x03)
+#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF
+
+#define CS42L52_IFACE_CTL2 0x07
+#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6)
+#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4)
+#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3)
+#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07
+
+#define CS42L52_ADC_PGA_A 0x08
+#define CS42L52_ADC_PGA_B 0x09
+#define CS42L52_ADC_SEL_SHIFT 5
+#define CS42L52_ADC_SEL_AIN1 0x00
+#define CS42L52_ADC_SEL_AIN2 0x01
+#define CS42L52_ADC_SEL_AIN3 0x02
+#define CS42L52_ADC_SEL_AIN4 0x03
+#define CS42L52_ADC_SEL_PGA 0x04
+
+#define CS42L52_ANALOG_HPF_CTL 0x0A
+#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3)
+#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0)
+
+#define CS42L52_ADC_HPF_FREQ 0x0B
+#define CS42L52_ADC_MISC_CTL 0x0C
+#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6)
+
+#define CS42L52_PB_CTL1 0x0D
+#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5
+#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00
+#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01
+#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02
+#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03
+#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04
+#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05
+#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06
+#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07
+#define CS42L52_PB_CTL1_INV_PCMB (1 << 3)
+#define CS42L52_PB_CTL1_INV_PCMA (1 << 2)
+#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1)
+#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0)
+#define CS42L52_PB_CTL1_MUTE_MASK 0xFFFD
+#define CS42L52_PB_CTL1_MUTE 3
+#define CS42L52_PB_CTL1_UNMUTE 0
+
+#define CS42L52_MISC_CTL 0x0E
+#define CS42L52_MISC_CTL_DEEMPH (1 << 2)
+#define CS42L52_MISC_CTL_DIGSFT (1 << 1)
+#define CS42L52_MISC_CTL_DIGZC (1 << 0)
+
+#define CS42L52_PB_CTL2 0x0F
+#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7)
+#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6)
+#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5)
+#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4)
+#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2)
+#define CS42L52_PB_CTL2_SPK_MONO (1 << 1)
+#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0)
+
+#define CS42L52_MICA_CTL 0x10
+#define CS42L52_MICB_CTL 0x11
+#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF
+#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6
+#define CS42L52_MIC_CTL_TYPE_MASK 0xDF
+#define CS42L52_MIC_CTL_TYPE_SHIFT 5
+
+
+#define CS42L52_PGAA_CTL 0x12
+#define CS42L52_PGAB_CTL 0x13
+#define CS42L52_PGAX_CTL_VOL_12DB 24
+#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/
+
+#define CS42L52_PASSTHRUA_VOL 0x14
+#define CS42L52_PASSTHRUB_VOL 0x15
+
+#define CS42L52_ADCA_VOL 0x16
+#define CS42L52_ADCB_VOL 0x17
+#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/
+#define CS42L52_ADCX_VOL_12DB 12
+#define CS42L52_ADCX_VOL_6DB 6
+
+#define CS42L52_ADCA_MIXER_VOL 0x18
+#define CS42L52_ADCB_MIXER_VOL 0x19
+#define CS42L52_ADC_MIXER_VOL_12DB 0x18
+
+#define CS42L52_PCMA_MIXER_VOL 0x1A
+#define CS42L52_PCMB_MIXER_VOL 0x1B
+
+#define CS42L52_BEEP_FREQ 0x1C
+#define CS42L52_BEEP_VOL 0x1D
+#define CS42L52_BEEP_TONE_CTL 0x1E
+#define CS42L52_BEEP_RATE_SHIFT 4
+#define CS42L52_BEEP_RATE_MASK 0x0F
+
+#define CS42L52_TONE_CTL 0x1F
+#define CS42L52_BEEP_EN_MASK 0x3F
+
+#define CS42L52_MASTERA_VOL 0x20
+#define CS42L52_MASTERB_VOL 0x21
+
+#define CS42L52_HPA_VOL 0x22
+#define CS42L52_HPB_VOL 0x23
+#define CS42L52_DEFAULT_HP_VOL 0xF0
+
+#define CS42L52_SPKA_VOL 0x24
+#define CS42L52_SPKB_VOL 0x25
+#define CS42L52_DEFAULT_SPK_VOL 0xF0
+
+#define CS42L52_ADC_PCM_MIXER 0x26
+
+#define CS42L52_LIMITER_CTL1 0x27
+#define CS42L52_LIMITER_CTL2 0x28
+#define CS42L52_LIMITER_AT_RATE 0x29
+
+#define CS42L52_ALC_CTL 0x2A
+#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7
+#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6
+#define CS42L52_ALC_CTL_FASTEST_ATTACK 0
+
+#define CS42L52_ALC_RATE 0x2B
+#define CS42L52_ALC_SLOWEST_RELEASE 0x3F
+
+#define CS42L52_ALC_THRESHOLD 0x2C
+#define CS42L52_ALC_MAX_RATE_SHIFT 5
+#define CS42L52_ALC_MIN_RATE_SHIFT 2
+#define CS42L52_ALC_RATE_0DB 0
+#define CS42L52_ALC_RATE_3DB 1
+#define CS42L52_ALC_RATE_6DB 2
+
+#define CS42L52_NOISE_GATE_CTL 0x2D
+#define CS42L52_NG_ENABLE_SHIFT 6
+#define CS42L52_NG_THRESHOLD_SHIFT 2
+#define CS42L52_NG_MIN_70DB 2
+#define CS42L52_NG_DELAY_SHIFT 0
+#define CS42L52_NG_DELAY_100MS 1
+
+#define CS42L52_CLK_STATUS 0x2E
+#define CS42L52_BATT_COMPEN 0x2F
+
+#define CS42L52_BATT_LEVEL 0x30
+#define CS42L52_SPK_STATUS 0x31
+#define CS42L52_SPK_STATUS_PIN_SHIFT 3
+#define CS42L52_SPK_STATUS_PIN_HIGH 1
+
+#define CS42L52_TEM_CTL 0x32
+#define CS42L52_TEM_CTL_SET 0x80
+#define CS42L52_THE_FOLDBACK 0x33
+#define CS42L52_CHARGE_PUMP 0x34
+#define CS42L52_CHARGE_PUMP_MASK 0xF0
+#define CS42L52_CHARGE_PUMP_SHIFT 4
+#define CS42L52_FIX_BITS1 0x3E
+#define CS42L52_FIX_BITS2 0x47
+
+#define CS42L52_MAX_REGISTER 0x34
+
+#endif
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 3686417f5ea..e0d45fdaa75 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -43,9 +43,6 @@ struct cs42l73_private {
};
static const struct reg_default cs42l73_reg_defaults[] = {
- { 1, 0x42 }, /* r01 - Device ID A&B */
- { 2, 0xA7 }, /* r02 - Device ID C&D */
- { 3, 0x30 }, /* r03 - Device ID E */
{ 6, 0xF1 }, /* r06 - Power Ctl 1 */
{ 7, 0xDF }, /* r07 - Power Ctl 2 */
{ 8, 0x3F }, /* r08 - Power Ctl 3 */
@@ -402,37 +399,37 @@ static const struct snd_kcontrol_new ear_amp_ctl =
static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume",
- CS42L73_HPAAVOL, CS42L73_HPBAVOL, 7,
- 0xffffffC1, 0x0C, hpaloa_tlv),
+ CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0,
+ 0x41, 0x4B, hpaloa_tlv),
SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL,
- CS42L73_LOBAVOL, 7, 0xffffffC1, 0x0C, hpaloa_tlv),
+ CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv),
SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL,
- CS42L73_MICBPREPGABVOL, 5, 0xffffff35,
- 0x34, micpga_tlv),
+ CS42L73_MICBPREPGABVOL, 5, 0x34,
+ 0x24, micpga_tlv),
SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL,
CS42L73_MICBPREPGABVOL, 6, 1, 1),
SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL,
- CS42L73_IPBDVOL, 7, 0xffffffA0, 0xA0, ipd_tlv),
+ CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv),
SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume",
- CS42L73_HLADVOL, CS42L73_HLBDVOL, 7, 0xffffffE5,
- 0xE4, hl_tlv),
+ CS42L73_HLADVOL, CS42L73_HLBDVOL,
+ 0, 0x34, 0xE4, hl_tlv),
SOC_SINGLE_TLV("ADC A Boost Volume",
CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv),
SOC_SINGLE_TLV("ADC B Boost Volume",
- CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
+ CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
- SOC_SINGLE_TLV("Speakerphone Digital Playback Volume",
- CS42L73_SPKDVOL, 0, 0xE4, 1, hl_tlv),
+ SOC_SINGLE_SX_TLV("Speakerphone Digital Volume",
+ CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv),
- SOC_SINGLE_TLV("Ear Speaker Digital Playback Volume",
- CS42L73_ESLDVOL, 0, 0xE4, 1, hl_tlv),
+ SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume",
+ CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv),
SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL,
CS42L73_HPBAVOL, 7, 1, 1),
@@ -599,17 +596,17 @@ static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0),
- SND_SOC_DAPM_AIF_OUT("XSPOUTL", "XSP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL, 0,
CS42L73_PWRCTL2, 1, 1),
- SND_SOC_DAPM_AIF_OUT("XSPOUTR", "XSP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL, 0,
CS42L73_PWRCTL2, 1, 1),
- SND_SOC_DAPM_AIF_OUT("ASPOUTL", "ASP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL, 0,
CS42L73_PWRCTL2, 3, 1),
- SND_SOC_DAPM_AIF_OUT("ASPOUTR", "ASP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL, 0,
CS42L73_PWRCTL2, 3, 1),
- SND_SOC_DAPM_AIF_OUT("VSPOUTL", "VSP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("VSPOUTL", NULL, 0,
CS42L73_PWRCTL2, 4, 1),
- SND_SOC_DAPM_AIF_OUT("VSPOUTR", "VSP Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("VSPOUTR", NULL, 0,
CS42L73_PWRCTL2, 4, 1),
SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -638,21 +635,21 @@ static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("VSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("VSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_AIF_IN("XSPINL", "XSP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0,
CS42L73_PWRCTL2, 0, 1),
- SND_SOC_DAPM_AIF_IN("XSPINR", "XSP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0,
CS42L73_PWRCTL2, 0, 1),
- SND_SOC_DAPM_AIF_IN("XSPINM", "XSP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0,
CS42L73_PWRCTL2, 0, 1),
- SND_SOC_DAPM_AIF_IN("ASPINL", "ASP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0,
CS42L73_PWRCTL2, 2, 1),
- SND_SOC_DAPM_AIF_IN("ASPINR", "ASP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0,
CS42L73_PWRCTL2, 2, 1),
- SND_SOC_DAPM_AIF_IN("ASPINM", "ASP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0,
CS42L73_PWRCTL2, 2, 1),
- SND_SOC_DAPM_AIF_IN("VSPIN", "VSP Playback", 0,
+ SND_SOC_DAPM_AIF_IN("VSPIN", NULL, 0,
CS42L73_PWRCTL2, 4, 1),
SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -776,6 +773,14 @@ static const struct snd_soc_dapm_route cs42l73_audio_map[] = {
{"HL Left Mixer", NULL, "VSPIN"},
{"HL Right Mixer", NULL, "VSPIN"},
+ {"ASPINL", NULL, "ASP Playback"},
+ {"ASPINM", NULL, "ASP Playback"},
+ {"ASPINR", NULL, "ASP Playback"},
+ {"XSPINL", NULL, "XSP Playback"},
+ {"XSPINM", NULL, "XSP Playback"},
+ {"XSPINR", NULL, "XSP Playback"},
+ {"VSPIN", NULL, "VSP Playback"},
+
/* Capture Paths */
{"MIC1", NULL, "MIC1 Bias"},
{"PGA Left Mux", "Mic 1", "MIC1"},
@@ -822,6 +827,13 @@ static const struct snd_soc_dapm_route cs42l73_audio_map[] = {
{"VSPOUTL", NULL, "VSPL Output Mixer"},
{"VSPOUTR", NULL, "VSPR Output Mixer"},
+
+ {"ASP Capture", NULL, "ASPOUTL"},
+ {"ASP Capture", NULL, "ASPOUTR"},
+ {"XSP Capture", NULL, "XSPOUTL"},
+ {"XSP Capture", NULL, "XSPOUTR"},
+ {"VSP Capture", NULL, "VSPOUTL"},
+ {"VSP Capture", NULL, "VSPOUTR"},
};
struct cs42l73_mclk_div {
@@ -1091,8 +1103,7 @@ static int cs42l73_pcm_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 snd_soc_codec *codec = dai->codec;
struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
int id = dai->id;
int mclk_coeff;
@@ -1429,25 +1440,7 @@ static struct i2c_driver cs42l73_i2c_driver = {
};
-static int __init cs42l73_modinit(void)
-{
- int ret;
- ret = i2c_add_driver(&cs42l73_i2c_driver);
- if (ret != 0) {
- pr_err("Failed to register CS42L73 I2C driver: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-module_init(cs42l73_modinit);
-
-static void __exit cs42l73_exit(void)
-{
- i2c_del_driver(&cs42l73_i2c_driver);
-}
-
-module_exit(cs42l73_exit);
+module_i2c_driver(cs42l73_i2c_driver);
MODULE_DESCRIPTION("ASoC CS42L73 driver");
MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 7843711729b..af5db708051 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -27,6 +28,7 @@
#include <sound/tlv.h>
/* DA7210 register space */
+#define DA7210_PAGE_CONTROL 0x00
#define DA7210_CONTROL 0x01
#define DA7210_STATUS 0x02
#define DA7210_STARTUP1 0x03
@@ -146,6 +148,7 @@
#define DA7210_DAI_EN (1 << 7)
/*PLL_DIV3 bit fields */
+#define DA7210_PLL_DIV_L_MASK (0xF << 0)
#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
#define DA7210_PLL_BYP (1 << 6)
@@ -162,12 +165,16 @@
#define DA7210_PLL_FS_48000 (0xB << 0)
#define DA7210_PLL_FS_88200 (0xE << 0)
#define DA7210_PLL_FS_96000 (0xF << 0)
+#define DA7210_MCLK_DET_EN (0x1 << 5)
+#define DA7210_MCLK_SRM_EN (0x1 << 6)
#define DA7210_PLL_EN (0x1 << 7)
/* SOFTMUTE bit fields */
#define DA7210_RAMP_EN (1 << 6)
/* CONTROL bit fields */
+#define DA7210_REG_EN (1 << 0)
+#define DA7210_BIAS_EN (1 << 2)
#define DA7210_NOISE_SUP_EN (1 << 3)
/* IN_GAIN bit fields */
@@ -206,6 +213,47 @@
#define DA7210_OUT2_OUTMIX_L (1 << 6)
#define DA7210_OUT2_EN (1 << 7)
+struct pll_div {
+ int fref;
+ int fout;
+ u8 div1;
+ u8 div2;
+ u8 div3;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL dividers table */
+static const struct pll_div da7210_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz */
+ { 12000000, 2822400, 0xE8, 0x6C, 0x2, 1}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xDF, 0x28, 0xC, 1}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDB, 0x0A, 0xD, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD4, 0x5A, 0x2, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBB, 0x43, 0x9, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xB9, 0x6D, 0xA, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xB8, 0xFB, 0xB, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz */
+ { 12000000, 3072000, 0xF3, 0x12, 0x7, 1}, /* MCLK=12Mhz */
+ { 13000000, 3072000, 0xE8, 0xFD, 0x5, 1}, /* MCLK=13Mhz */
+ { 13500000, 3072000, 0xE4, 0x82, 0x3, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 3072000, 0xDD, 0x3A, 0x0, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 3072000, 0xC1, 0xEB, 0x8, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 3072000, 0xBF, 0xEC, 0x0, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 3072000, 0xBF, 0x70, 0x0, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ { 12000000, 2822400, 0xED, 0xBF, 0x5, 0}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xE4, 0x13, 0x0, 0}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDF, 0xC6, 0x8, 0}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD8, 0xCA, 0x1, 0}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBE, 0x97, 0x9, 0}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xBC, 0xAC, 0xD, 0}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xBC, 0x35, 0xE, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA7210_CLKSRC_MCLK
+};
+
#define DA7210_VERSION "0.0.1"
/*
@@ -628,9 +676,12 @@ static const struct snd_soc_dapm_route da7210_audio_map[] = {
/* Codec private data */
struct da7210_priv {
struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
};
static struct reg_default da7210_reg_defaults[] = {
+ { 0x00, 0x00 },
{ 0x01, 0x11 },
{ 0x03, 0x00 },
{ 0x04, 0x00 },
@@ -713,10 +764,10 @@ static int da7210_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 snd_soc_codec *codec = dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
u32 dai_cfg1;
- u32 fs, bypass;
+ u32 fs, sysclk;
/* set DAI source to Left and Right ADC */
snd_soc_write(codec, DA7210_DAI_SRC_SEL,
@@ -749,43 +800,43 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) {
case 8000:
fs = DA7210_PLL_FS_8000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 11025:
fs = DA7210_PLL_FS_11025;
- bypass = 0;
+ sysclk = 2822400;
break;
case 12000:
fs = DA7210_PLL_FS_12000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 16000:
fs = DA7210_PLL_FS_16000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 22050:
fs = DA7210_PLL_FS_22050;
- bypass = 0;
+ sysclk = 2822400;
break;
case 32000:
fs = DA7210_PLL_FS_32000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 44100:
fs = DA7210_PLL_FS_44100;
- bypass = 0;
+ sysclk = 2822400;
break;
case 48000:
fs = DA7210_PLL_FS_48000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
case 88200:
fs = DA7210_PLL_FS_88200;
- bypass = 0;
+ sysclk = 2822400;
break;
case 96000:
fs = DA7210_PLL_FS_96000;
- bypass = DA7210_PLL_BYP;
+ sysclk = 3072000;
break;
default:
return -EINVAL;
@@ -795,8 +846,26 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, bypass);
+ if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) {
+ /* PLL mode, disable PLL bypass */
+ snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+
+ if (!da7210->master) {
+ /* PLL slave mode, also enable SRM */
+ snd_soc_update_bits(codec, DA7210_PLL,
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN),
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN));
+ }
+ } else {
+ /* PLL bypass mode, enable PLL bypass and Auto Detection */
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+ DA7210_MCLK_DET_EN);
+ snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ DA7210_PLL_BYP);
+ }
/* Enable active mode */
snd_soc_update_bits(codec, DA7210_STARTUP1,
DA7210_SC_MST_EN, DA7210_SC_MST_EN);
@@ -810,17 +879,24 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
u32 dai_cfg1;
u32 dai_cfg3;
dai_cfg1 = 0x7f & snd_soc_read(codec, DA7210_DAI_CFG1);
dai_cfg3 = 0xfc & snd_soc_read(codec, DA7210_DAI_CFG3);
+ if ((snd_soc_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
+ (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ return -EINVAL;
+
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
+ da7210->master = 1;
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
+ da7210->master = 0;
dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
break;
default:
@@ -872,10 +948,101 @@ static int da7210_mute(struct snd_soc_dai *dai, int mute)
#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case DA7210_CLKSRC_MCLK:
+ switch (freq) {
+ case 12000000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da7210->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/**
+ * da7210_set_dai_pll :Configure the codec PLL
+ * @param codec_dai : pointer to codec DAI
+ * @param pll_id : da7210 has only one pll, so pll_id is always zero
+ * @param fref : MCLK frequency, should be < 20MHz
+ * @param fout : FsDM value, Refer page 44 & 45 of datasheet
+ * @return int : Zero for success, negative error code for error
+ *
+ * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz,
+ * 19.2MHz, 19.6MHz and 19.8MHz
+ */
+static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+ u8 pll_div1, pll_div2, pll_div3, cnt;
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da7210->master)
+ fout = 2822400;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) {
+ /* check fref, mode and fout */
+ if ((fref == da7210_pll_div[cnt].fref) &&
+ (da7210->master == da7210_pll_div[cnt].mode) &&
+ (fout == da7210_pll_div[cnt].fout)) {
+ /* all match, pick up divisors */
+ pll_div1 = da7210_pll_div[cnt].div1;
+ pll_div2 = da7210_pll_div[cnt].div2;
+ pll_div3 = da7210_pll_div[cnt].div3;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da7210_pll_div))
+ goto err;
+
+ /* Disable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ /* Write PLL dividers */
+ snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1);
+ snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2);
+ snd_soc_update_bits(codec, DA7210_PLL_DIV3,
+ DA7210_PLL_DIV_L_MASK, pll_div3);
+
+ /* Enable PLL */
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+
+ /* Enable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ DA7210_SC_MST_EN);
+ return 0;
+err:
+ dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref);
+ return -EINVAL;
+}
+
/* DAI operations */
static const struct snd_soc_dai_ops da7210_dai_ops = {
.hw_params = da7210_hw_params,
.set_fmt = da7210_set_dai_fmt,
+ .set_sysclk = da7210_set_dai_sysclk,
+ .set_pll = da7210_set_dai_pll,
.digital_mute = da7210_mute,
};
@@ -915,24 +1082,11 @@ static int da7210_probe(struct snd_soc_codec *codec)
return ret;
}
- /* FIXME
- *
- * This driver use fixed value here
- * And below settings expects MCLK = 12.288MHz
- *
- * When you select different MCLK, please check...
- * DA7210_PLL_DIV1 val
- * DA7210_PLL_DIV2 val
- * DA7210_PLL_DIV3 val
- * DA7210_PLL_DIV3 :: DA7210_MCLK_RANGExxx
- */
+ da7210->mclk_rate = 0; /* This will be set from set_sysclk() */
+ da7210->master = 0; /* This will be set from set_fmt() */
- /*
- * make sure that DA7210 use bypass mode before start up
- */
- snd_soc_write(codec, DA7210_STARTUP1, 0);
- snd_soc_write(codec, DA7210_PLL_DIV3,
- DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+ /* Enable internal regulator & bias current */
+ snd_soc_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
/*
* ADC settings
@@ -1007,34 +1161,13 @@ static int da7210_probe(struct snd_soc_codec *codec)
/* Enable Aux2 */
snd_soc_write(codec, DA7210_AUX2, DA7210_AUX2_EN);
+ /* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
+ snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+ DA7210_PLL_BYP);
+
/* Diable PLL and bypass it */
snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
- /*
- * If 48kHz sound came, it use bypass mode,
- * and when it is 44.1kHz, it use PLL.
- *
- * This time, this driver sets PLL always ON
- * and controls bypass/PLL mode by switching
- * DA7210_PLL_DIV3 :: DA7210_PLL_BYP bit.
- * see da7210_hw_params
- */
- snd_soc_write(codec, DA7210_PLL_DIV1, 0xE5); /* MCLK = 12.288MHz */
- snd_soc_write(codec, DA7210_PLL_DIV2, 0x99);
- snd_soc_write(codec, DA7210_PLL_DIV3, 0x0A |
- DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
-
- /* As suggested by Dialog */
- /* unlock */
- regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK, 0x8B);
- regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK, 0xB4);
- regmap_write(da7210->regmap, DA7210_A_PLL1, 0x01);
- regmap_write(da7210->regmap, DA7210_A_CP_MODE, 0x7C);
- /* re-lock */
- regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK, 0x00);
- regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK, 0x00);
-
/* Activate all enabled subsystem */
snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
@@ -1055,7 +1188,26 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
.num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
};
-static struct regmap_config da7210_regmap = {
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static struct reg_default da7210_regmap_i2c_patch[] = {
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+};
+
+static const struct regmap_config da7210_regmap_config_i2c = {
.reg_bits = 8,
.val_bits = 8,
@@ -1066,7 +1218,6 @@ static struct regmap_config da7210_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1080,13 +1231,18 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, da7210);
- da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap);
+ da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
if (IS_ERR(da7210->regmap)) {
ret = PTR_ERR(da7210->regmap);
dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch,
+ ARRAY_SIZE(da7210_regmap_i2c_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_da7210, &da7210_dai, 1);
if (ret < 0) {
@@ -1119,7 +1275,7 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
/* I2C codec control layer */
static struct i2c_driver da7210_i2c_driver = {
.driver = {
- .name = "da7210-codec",
+ .name = "da7210",
.owner = THIS_MODULE,
},
.probe = da7210_i2c_probe,
@@ -1128,12 +1284,112 @@ static struct i2c_driver da7210_i2c_driver = {
};
#endif
+#if defined(CONFIG_SPI_MASTER)
+
+static struct reg_default da7210_regmap_spi_patch[] = {
+ /* Dummy read to give two pulses over nCS for SPI */
+ { DA7210_AUX2, 0x00 },
+ { DA7210_AUX2, 0x00 },
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to set PAGE1 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x80 },
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+ /* to set back PAGE0 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x00 },
+};
+
+static const struct regmap_config da7210_regmap_config_spi = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = 0x01,
+ .write_flag_mask = 0x00,
+
+ .reg_defaults = da7210_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+ .volatile_reg = da7210_volatile_register,
+ .readable_reg = da7210_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit da7210_spi_probe(struct spi_device *spi)
+{
+ struct da7210_priv *da7210;
+ int ret;
+
+ da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv),
+ GFP_KERNEL);
+ if (!da7210)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, da7210);
+ da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi);
+ if (IS_ERR(da7210->regmap)) {
+ ret = PTR_ERR(da7210->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch,
+ ARRAY_SIZE(da7210_regmap_spi_patch));
+ if (ret != 0)
+ dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_da7210, &da7210_dai, 1);
+ if (ret < 0)
+ goto err_regmap;
+
+ return ret;
+
+err_regmap:
+ regmap_exit(da7210->regmap);
+
+ return ret;
+}
+
+static int __devexit da7210_spi_remove(struct spi_device *spi)
+{
+ struct da7210_priv *da7210 = spi_get_drvdata(spi);
+ snd_soc_unregister_codec(&spi->dev);
+ regmap_exit(da7210->regmap);
+ return 0;
+}
+
+static struct spi_driver da7210_spi_driver = {
+ .driver = {
+ .name = "da7210",
+ .owner = THIS_MODULE,
+ },
+ .probe = da7210_spi_probe,
+ .remove = __devexit_p(da7210_spi_remove)
+};
+#endif
+
static int __init da7210_modinit(void)
{
int ret = 0;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&da7210_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&da7210_spi_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n",
+ ret);
+ }
+#endif
return ret;
}
module_init(da7210_modinit);
@@ -1143,6 +1399,9 @@ static void __exit da7210_exit(void)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&da7210_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&da7210_spi_driver);
+#endif
}
module_exit(da7210_exit);
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 4624e752a18..85d9cabe6d5 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -164,8 +164,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
uint32_t val;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec =rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
switch (params_rate(params)) {
case 8000:
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
new file mode 100644
index 00000000000..802b9f176b1
--- /dev/null
+++ b/sound/soc/codecs/lm49453.c
@@ -0,0 +1,1550 @@
+/*
+ * lm49453.c - LM49453 ALSA Soc Audio driver
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * Initially based on sound/soc/codecs/wm8350.c
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "lm49453.h"
+
+static struct reg_default lm49453_reg_defs[] = {
+ { 0, 0x00 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x00 },
+ { 5, 0x00 },
+ { 6, 0x00 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x00 },
+ { 11, 0x00 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x00 },
+ { 15, 0x00 },
+ { 16, 0x00 },
+ { 17, 0x00 },
+ { 18, 0x00 },
+ { 19, 0x00 },
+ { 20, 0x00 },
+ { 21, 0x00 },
+ { 22, 0x00 },
+ { 23, 0x00 },
+ { 32, 0x00 },
+ { 33, 0x00 },
+ { 35, 0x00 },
+ { 36, 0x00 },
+ { 37, 0x00 },
+ { 46, 0x00 },
+ { 48, 0x00 },
+ { 49, 0x00 },
+ { 51, 0x00 },
+ { 56, 0x00 },
+ { 58, 0x00 },
+ { 59, 0x00 },
+ { 60, 0x00 },
+ { 61, 0x00 },
+ { 62, 0x00 },
+ { 63, 0x00 },
+ { 64, 0x00 },
+ { 65, 0x00 },
+ { 66, 0x00 },
+ { 67, 0x00 },
+ { 68, 0x00 },
+ { 69, 0x00 },
+ { 70, 0x00 },
+ { 71, 0x00 },
+ { 72, 0x00 },
+ { 73, 0x00 },
+ { 74, 0x00 },
+ { 75, 0x00 },
+ { 76, 0x00 },
+ { 77, 0x00 },
+ { 78, 0x00 },
+ { 79, 0x00 },
+ { 80, 0x00 },
+ { 81, 0x00 },
+ { 82, 0x00 },
+ { 83, 0x00 },
+ { 85, 0x00 },
+ { 85, 0x00 },
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x00 },
+ { 90, 0x00 },
+ { 91, 0x00 },
+ { 92, 0x00 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+ { 96, 0x01 },
+ { 97, 0x00 },
+ { 98, 0x00 },
+ { 99, 0x00 },
+ { 100, 0x00 },
+ { 101, 0x00 },
+ { 102, 0x00 },
+ { 103, 0x01 },
+ { 105, 0x01 },
+ { 106, 0x00 },
+ { 107, 0x01 },
+ { 107, 0x00 },
+ { 108, 0x00 },
+ { 109, 0x00 },
+ { 110, 0x00 },
+ { 111, 0x02 },
+ { 112, 0x02 },
+ { 113, 0x00 },
+ { 121, 0x80 },
+ { 122, 0xBB },
+ { 123, 0x80 },
+ { 124, 0xBB },
+ { 128, 0x00 },
+ { 130, 0x00 },
+ { 131, 0x00 },
+ { 132, 0x00 },
+ { 133, 0x0A },
+ { 134, 0x0A },
+ { 135, 0x0A },
+ { 136, 0x0F },
+ { 137, 0x00 },
+ { 138, 0x73 },
+ { 139, 0x33 },
+ { 140, 0x73 },
+ { 141, 0x33 },
+ { 142, 0x73 },
+ { 143, 0x33 },
+ { 144, 0x73 },
+ { 145, 0x33 },
+ { 146, 0x73 },
+ { 147, 0x33 },
+ { 148, 0x73 },
+ { 149, 0x33 },
+ { 150, 0x73 },
+ { 151, 0x33 },
+ { 152, 0x00 },
+ { 153, 0x00 },
+ { 154, 0x00 },
+ { 155, 0x00 },
+ { 176, 0x00 },
+ { 177, 0x00 },
+ { 178, 0x00 },
+ { 179, 0x00 },
+ { 180, 0x00 },
+ { 181, 0x00 },
+ { 182, 0x00 },
+ { 183, 0x00 },
+ { 184, 0x00 },
+ { 185, 0x00 },
+ { 186, 0x00 },
+ { 189, 0x00 },
+ { 188, 0x00 },
+ { 194, 0x00 },
+ { 195, 0x00 },
+ { 196, 0x00 },
+ { 197, 0x00 },
+ { 200, 0x00 },
+ { 201, 0x00 },
+ { 202, 0x00 },
+ { 203, 0x00 },
+ { 204, 0x00 },
+ { 205, 0x00 },
+ { 208, 0x00 },
+ { 209, 0x00 },
+ { 210, 0x00 },
+ { 211, 0x00 },
+ { 213, 0x00 },
+ { 214, 0x00 },
+ { 215, 0x00 },
+ { 216, 0x00 },
+ { 217, 0x00 },
+ { 218, 0x00 },
+ { 219, 0x00 },
+ { 221, 0x00 },
+ { 222, 0x00 },
+ { 224, 0x00 },
+ { 225, 0x00 },
+ { 226, 0x00 },
+ { 227, 0x00 },
+ { 228, 0x00 },
+ { 229, 0x00 },
+ { 230, 0x13 },
+ { 231, 0x00 },
+ { 232, 0x80 },
+ { 233, 0x0C },
+ { 234, 0xDD },
+ { 235, 0x00 },
+ { 236, 0x04 },
+ { 237, 0x00 },
+ { 238, 0x00 },
+ { 239, 0x00 },
+ { 240, 0x00 },
+ { 241, 0x00 },
+ { 242, 0x00 },
+ { 243, 0x00 },
+ { 244, 0x00 },
+ { 245, 0x00 },
+ { 248, 0x00 },
+ { 249, 0x00 },
+ { 254, 0x00 },
+ { 255, 0x00 },
+};
+
+/* codec private data */
+struct lm49453_priv {
+ struct regmap *regmap;
+ int fs_rate;
+};
+
+/* capture path controls */
+
+static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"};
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
+ lm49453_mic2mode_text);
+
+static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"};
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
+ LM49453_P0_DIGITAL_MIC1_CONFIG_REG,
+ 7, lm49453_dmic_cfg_text);
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
+ LM49453_P0_DIGITAL_MIC2_CONFIG_REG,
+ 7, lm49453_dmic_cfg_text);
+
+/* MUX Controls */
+static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" };
+
+static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" };
+
+static const struct soc_enum lm49453_adcl_enum =
+ SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 0,
+ ARRAY_SIZE(lm49453_adcl_mux_text),
+ lm49453_adcl_mux_text);
+
+static const struct soc_enum lm49453_adcr_enum =
+ SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 1,
+ ARRAY_SIZE(lm49453_adcr_mux_text),
+ lm49453_adcr_mux_text);
+
+static const struct snd_kcontrol_new lm49453_adcl_mux_control =
+ SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum);
+
+static const struct snd_kcontrol_new lm49453_adcr_mux_control =
+ SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum);
+
+static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
+};
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
+/* Sidetone supports mono only */
+SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
+ 0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
+ 0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
+ 0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
+ 0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
+ 0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
+ 0, 0x3F, 0, digital_tlv),
+};
+
+static const struct snd_kcontrol_new lm49453_snd_controls[] = {
+ /* mic1 and mic2 supports mono only */
+ SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6,
+ 0, digital_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6,
+ 0, digital_tlv),
+
+ SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
+ LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
+ LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv),
+
+ SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
+ SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
+ SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum),
+
+ /* Capture path filter enable */
+ SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 0, 1, 0),
+ SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 1, 1, 0),
+ SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 2, 1, 0),
+
+ SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
+ LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
+ LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
+ LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv),
+
+ SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ 0, 6, 0, digital_tlv),
+
+ SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 6, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 6, 3, 0, port_tlv),
+
+ SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 2, 3, 0, port_tlv),
+
+ SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 2, 1, 0),
+ SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 2, 1, 0)
+
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = {
+
+ /* All end points HP,EP, LS, Lineout and Haptic */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("EPOUT"),
+ SND_SOC_DAPM_OUTPUT("LSOUTL"),
+ SND_SOC_DAPM_OUTPUT("LSOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTR"),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0),
+
+ /* playback path driver enables */
+ SND_SOC_DAPM_OUT_DRV("Headset Switch",
+ LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Earpiece Switch",
+ LM49453_P0_EP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+
+
+ SND_SOC_DAPM_PGA("AUXL Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUXR Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcl_mux_control),
+ SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcr_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic1 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic2 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control),
+
+ /* AIF */
+ SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 2, 0),
+ SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 6, 0),
+
+ SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 7, 0),
+
+ /* Port1 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Port2 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Sidetone Mixer */
+ SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_sidetone_mixer_controls,
+ ARRAY_SIZE(lm49453_sidetone_mixer_controls)),
+
+ /* DAC MIXERS */
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_left_mixer,
+ ARRAY_SIZE(lm49453_headset_left_mixer)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_right_mixer,
+ ARRAY_SIZE(lm49453_headset_right_mixer)),
+ SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_left_mixer,
+ ARRAY_SIZE(lm49453_lineout_left_mixer)),
+ SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_right_mixer,
+ ARRAY_SIZE(lm49453_lineout_right_mixer)),
+ SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_left_mixer,
+ ARRAY_SIZE(lm49453_speaker_left_mixer)),
+ SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_right_mixer,
+ ARRAY_SIZE(lm49453_speaker_right_mixer)),
+ SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_left_mixer,
+ ARRAY_SIZE(lm49453_haptic_left_mixer)),
+ SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_right_mixer,
+ ARRAY_SIZE(lm49453_haptic_right_mixer)),
+
+ /* Capture Mixer */
+ SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx1_mixer,
+ ARRAY_SIZE(lm49453_port1_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx2_mixer,
+ ARRAY_SIZE(lm49453_port1_tx2_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx3_mixer,
+ ARRAY_SIZE(lm49453_port1_tx3_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx4_mixer,
+ ARRAY_SIZE(lm49453_port1_tx4_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx5_mixer,
+ ARRAY_SIZE(lm49453_port1_tx5_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx6_mixer,
+ ARRAY_SIZE(lm49453_port1_tx6_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx7_mixer,
+ ARRAY_SIZE(lm49453_port1_tx7_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx8_mixer,
+ ARRAY_SIZE(lm49453_port1_tx8_mixer)),
+
+ SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx1_mixer,
+ ARRAY_SIZE(lm49453_port2_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx2_mixer,
+ ARRAY_SIZE(lm49453_port2_tx2_mixer)),
+};
+
+static const struct snd_soc_dapm_route lm49453_audio_map[] = {
+ /* Port SDI mapping */
+ { "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" },
+
+ { "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" },
+ { "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" },
+
+ /* HP mapping */
+ { "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ { "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPL Mixer", "ADCL Switch", "ADC Left" },
+ { "HPL Mixer", "ADCR Switch", "ADC Right" },
+ { "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HPL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPL DAC", NULL, "HPL Mixer" },
+
+ { "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPR Mixer", "ADCL Switch", "ADC Left" },
+ { "HPR Mixer", "ADCR Switch", "ADC Right" },
+ { "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" },
+ { "HPR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPR DAC", NULL, "HPR Mixer" },
+
+ { "HPOUTL", "Headset Switch", "HPL DAC"},
+ { "HPOUTR", "Headset Switch", "HPR DAC"},
+
+ /* EP map */
+ { "EPOUT", "Earpiece Switch", "HPL DAC" },
+
+ /* Speaker map */
+ { "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSL Mixer", "ADCL Switch", "ADC Left" },
+ { "LSL Mixer", "ADCR Switch", "ADC Right" },
+ { "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSL DAC", NULL, "LSL Mixer" },
+
+ { "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSR Mixer", "ADCL Switch", "ADC Left" },
+ { "LSR Mixer", "ADCR Switch", "ADC Right" },
+ { "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSR DAC", NULL, "LSR Mixer" },
+
+ { "LSOUTL", "Speaker Left Switch", "LSL DAC"},
+ { "LSOUTR", "Speaker Left Switch", "LSR DAC"},
+
+ /* Haptic map */
+ { "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAL Mixer", "ADCL Switch", "ADC Left" },
+ { "HAL Mixer", "ADCR Switch", "ADC Right" },
+ { "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HAL DAC", NULL, "HAL Mixer" },
+
+ { "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAR Mixer", "ADCL Switch", "ADC Left" },
+ { "HAR Mixer", "ADCR Switch", "ADC Right" },
+ { "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAR Mixer", "Sideton Switch", "Sidetone" },
+
+ { "HAR DAC", NULL, "HAR Mixer" },
+
+ { "HAOUTL", "Haptic Left Switch", "HAL DAC" },
+ { "HAOUTR", "Haptic Right Switch", "HAR DAC" },
+
+ /* Lineout map */
+ { "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOL Mixer", "ADCL Switch", "ADC Left" },
+ { "LOL Mixer", "ADCR Switch", "ADC Right" },
+ { "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOL DAC", NULL, "LOL Mixer" },
+
+ { "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOR Mixer", "ADCL Switch", "ADC Left" },
+ { "LOR Mixer", "ADCR Switch", "ADC Right" },
+ { "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOR DAC", NULL, "LOR Mixer" },
+
+ { "LOOUTL", NULL, "LOL DAC" },
+ { "LOOUTR", NULL, "LOR DAC" },
+
+ /* TX map */
+ /* Port1 mappings */
+ { "Port1_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_3 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_3 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_4 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_4 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_5 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_5 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_6 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_6 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_7 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_7 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_8 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_8 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "P1_1_TX", NULL, "Port1_1 Mixer" },
+ { "P1_2_TX", NULL, "Port1_2 Mixer" },
+ { "P1_3_TX", NULL, "Port1_3 Mixer" },
+ { "P1_4_TX", NULL, "Port1_4 Mixer" },
+ { "P1_5_TX", NULL, "Port1_5 Mixer" },
+ { "P1_6_TX", NULL, "Port1_6 Mixer" },
+ { "P1_7_TX", NULL, "Port1_7 Mixer" },
+ { "P1_8_TX", NULL, "Port1_8 Mixer" },
+
+ { "P2_1_TX", NULL, "Port2_1 Mixer" },
+ { "P2_2_TX", NULL, "Port2_2 Mixer" },
+
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"},
+
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"},
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"},
+
+ { "Mic1 Input", NULL, "AMIC1" },
+ { "Mic2 Input", NULL, "AMIC2" },
+
+ { "AUXL Input", NULL, "AUXL" },
+ { "AUXR Input", NULL, "AUXR" },
+
+ /* AUX connections */
+ { "ADCL Mux", "Aux_L", "AUXL Input" },
+ { "ADCL Mux", "MIC1", "Mic1 Input" },
+
+ { "ADCR Mux", "Aux_R", "AUXR Input" },
+ { "ADCR Mux", "MIC2", "Mic2 Input" },
+
+ /* ADC connection */
+ { "ADC Left", NULL, "ADCL Mux"},
+ { "ADC Right", NULL, "ADCR Mux"},
+
+ { "DMIC1 Left", NULL, "DMIC1DAT"},
+ { "DMIC1 Right", NULL, "DMIC1DAT"},
+ { "DMIC2 Left", NULL, "DMIC2DAT"},
+ { "DMIC2 Right", NULL, "DMIC2DAT"},
+
+ /* Sidetone map */
+ { "Sidetone Mixer", NULL, "ADC Left" },
+ { "Sidetone Mixer", NULL, "ADC Right" },
+ { "Sidetone Mixer", NULL, "DMIC1 Left" },
+ { "Sidetone Mixer", NULL, "DMIC1 Right" },
+ { "Sidetone Mixer", NULL, "DMIC2 Left" },
+ { "Sidetone Mixer", NULL, "DMIC2 Right" },
+
+ { "Sidetone", "Sidetone Switch", "Sidetone Mixer" },
+};
+
+static int lm49453_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 lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ u16 clk_div = 0;
+
+ lm49453->fs_rate = params_rate(params);
+
+ /* Setting DAC clock dividers based on substream sample rate. */
+ switch (lm49453->fs_rate) {
+ case 8000:
+ case 16000:
+ case 32000:
+ case 24000:
+ case 48000:
+ clk_div = 256;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk_div = 216;
+ break;
+ case 96000:
+ clk_div = 127;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
+ snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+
+ return 0;
+}
+
+static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ u16 aif_val;
+ int mode = 0;
+ int clk_phase = 0;
+ int clk_shift = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aif_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
+ LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5),
+ (aif_val | mode | clk_phase));
+
+ snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+
+ return 0;
+}
+
+static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 pll_clk = 0;
+
+ switch (freq) {
+ case 12288000:
+ case 26000000:
+ case 19200000:
+ /* pll clk slection */
+ pll_clk = 0;
+ break;
+ case 48000:
+ case 32576:
+ /* fll clk slection */
+ pll_clk = BIT(4);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+
+ return 0;
+}
+
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+ (mute ? (BIT(1)|BIT(0)) : 0));
+ return 0;
+}
+
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+ (mute ? (BIT(3)|BIT(2)) : 0));
+ return 0;
+}
+
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+ (mute ? (BIT(5)|BIT(4)) : 0));
+ return 0;
+}
+
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4),
+ (mute ? BIT(4) : 0));
+ return 0;
+}
+
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+ (mute ? (BIT(7)|BIT(6)) : 0));
+ return 0;
+}
+
+static int lm49453_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ regcache_sync(lm49453->regmap);
+
+ snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ LM49453_PMC_SETUP_CHIP_EN, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+/* Formates supported by LM49453 driver. */
+#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops lm49453_headset_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .digital_mute = lm49453_hp_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .digital_mute = lm49453_ls_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .digital_mute = lm49453_ha_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_ep_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .digital_mute = lm49453_ep_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
+ .hw_params = lm49453_hw_params,
+ .set_sysclk = lm49453_set_dai_sysclk,
+ .set_fmt = lm49453_set_dai_fmt,
+ .digital_mute = lm49453_lo_mute,
+};
+
+/* LM49453 dai structure. */
+static const struct snd_soc_dai_driver lm49453_dai[] = {
+ {
+ .name = "LM49453 Headset",
+ .playback = {
+ .stream_name = "Headset",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_headset_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "LM49453 Speaker",
+ .playback = {
+ .stream_name = "Speaker",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_speaker_dai_ops,
+ },
+ {
+ .name = "LM49453 Haptic",
+ .playback = {
+ .stream_name = "Haptic",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_haptic_dai_ops,
+ },
+ {
+ .name = "LM49453 Earpiece",
+ .playback = {
+ .stream_name = "Earpiece",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_ep_dai_ops,
+ },
+ {
+ .name = "LM49453 line out",
+ .playback = {
+ .stream_name = "Lineout",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_lineout_dai_ops,
+ },
+};
+
+static int lm49453_suspend(struct snd_soc_codec *codec)
+{
+ lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int lm49453_resume(struct snd_soc_codec *codec)
+{
+ lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+static int lm49453_probe(struct snd_soc_codec *codec)
+{
+ struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ codec->control_data = lm49453->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* power down chip */
+static int lm49453_remove(struct snd_soc_codec *codec)
+{
+ lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
+ .probe = lm49453_probe,
+ .remove = lm49453_remove,
+ .suspend = lm49453_suspend,
+ .resume = lm49453_resume,
+ .set_bias_level = lm49453_set_bias_level,
+ .controls = lm49453_snd_controls,
+ .num_controls = ARRAY_SIZE(lm49453_snd_controls),
+ .dapm_widgets = lm49453_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets),
+ .dapm_routes = lm49453_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map),
+ .idle_bias_off = true,
+};
+
+static const struct regmap_config lm49453_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = LM49453_MAX_REGISTER,
+ .reg_defaults = lm49453_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct lm49453_priv *lm49453;
+ int ret = 0;
+
+ lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv),
+ GFP_KERNEL);
+
+ if (lm49453 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, lm49453);
+
+ lm49453->regmap = regmap_init_i2c(i2c, &lm49453_regmap_config);
+ if (IS_ERR(lm49453->regmap)) {
+ ret = PTR_ERR(lm49453->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_lm49453,
+ lm49453_dai, ARRAY_SIZE(lm49453_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ regmap_exit(lm49453->regmap);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int __devexit lm49453_i2c_remove(struct i2c_client *client)
+{
+ struct lm49453_priv *lm49453 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ regmap_exit(lm49453->regmap);
+ return 0;
+}
+
+static const struct i2c_device_id lm49453_i2c_id[] = {
+ { "lm49453", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
+
+static struct i2c_driver lm49453_i2c_driver = {
+ .driver = {
+ .name = "lm49453",
+ .owner = THIS_MODULE,
+ },
+ .probe = lm49453_i2c_probe,
+ .remove = __devexit_p(lm49453_i2c_remove),
+ .id_table = lm49453_i2c_id,
+};
+
+module_i2c_driver(lm49453_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC LM49453 driver");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
new file mode 100644
index 00000000000..a63cfa5c088
--- /dev/null
+++ b/sound/soc/codecs/lm49453.h
@@ -0,0 +1,380 @@
+/*
+ * lm49453.h - LM49453 ALSA Soc Audio drive
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+
+#ifndef _LM49453_H
+#define _LM49453_H
+
+#include <linux/bitops.h>
+
+/* LM49453_P0 register space for page0 */
+#define LM49453_P0_PMC_SETUP_REG 0x00
+#define LM49453_P0_PLL_CLK_SEL1_REG 0x01
+#define LM49453_P0_PLL_CLK_SEL2_REG 0x02
+#define LM49453_P0_PMC_CLK_DIV_REG 0x03
+#define LM49453_P0_HSDET_CLK_DIV_REG 0x04
+#define LM49453_P0_DMIC_CLK_DIV_REG 0x05
+#define LM49453_P0_ADC_CLK_DIV_REG 0x06
+#define LM49453_P0_DAC_OT_CLK_DIV_REG 0x07
+#define LM49453_P0_PLL_HF_M_REG 0x08
+#define LM49453_P0_PLL_LF_M_REG 0x09
+#define LM49453_P0_PLL_NL_REG 0x0A
+#define LM49453_P0_PLL_N_MODL_REG 0x0B
+#define LM49453_P0_PLL_N_MODH_REG 0x0C
+#define LM49453_P0_PLL_P1_REG 0x0D
+#define LM49453_P0_PLL_P2_REG 0x0E
+#define LM49453_P0_FLL_REF_FREQL_REG 0x0F
+#define LM49453_P0_FLL_REF_FREQH_REG 0x10
+#define LM49453_P0_VCO_TARGETLL_REG 0x11
+#define LM49453_P0_VCO_TARGETLH_REG 0x12
+#define LM49453_P0_VCO_TARGETHL_REG 0x13
+#define LM49453_P0_VCO_TARGETHH_REG 0x14
+#define LM49453_P0_PLL_CONFIG_REG 0x15
+#define LM49453_P0_DAC_CLK_SEL_REG 0x16
+#define LM49453_P0_DAC_HP_CLK_DIV_REG 0x17
+
+/* Analog Mixer Input Stages */
+#define LM49453_P0_MICL_REG 0x20
+#define LM49453_P0_MICR_REG 0x21
+#define LM49453_P0_EP_REG 0x24
+#define LM49453_P0_DIS_PKVL_FB_REG 0x25
+
+/* Analog Mixer Output Stages */
+#define LM49453_P0_ANALOG_MIXER_ADC_REG 0x2E
+
+/*ADC or DAC */
+#define LM49453_P0_ADC_DSP_REG 0x30
+#define LM49453_P0_DAC_DSP_REG 0x31
+
+/* EFFECTS ENABLES */
+#define LM49453_P0_ADC_FX_ENABLES_REG 0x33
+
+/* GPIO */
+#define LM49453_P0_GPIO1_REG 0x38
+#define LM49453_P0_GPIO2_REG 0x39
+#define LM49453_P0_GPIO3_REG 0x3A
+#define LM49453_P0_HAP_CTL_REG 0x3B
+#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG 0x3C
+#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG 0x3D
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG 0x3E
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG 0x3F
+
+/* DIGITAL MIXER */
+#define LM49453_P0_DMIX_CLK_SEL_REG 0x40
+#define LM49453_P0_PORT1_RX_LVL1_REG 0x41
+#define LM49453_P0_PORT1_RX_LVL2_REG 0x42
+#define LM49453_P0_PORT2_RX_LVL_REG 0x43
+#define LM49453_P0_PORT1_TX1_REG 0x44
+#define LM49453_P0_PORT1_TX2_REG 0x45
+#define LM49453_P0_PORT1_TX3_REG 0x46
+#define LM49453_P0_PORT1_TX4_REG 0x47
+#define LM49453_P0_PORT1_TX5_REG 0x48
+#define LM49453_P0_PORT1_TX6_REG 0x49
+#define LM49453_P0_PORT1_TX7_REG 0x4A
+#define LM49453_P0_PORT1_TX8_REG 0x4B
+#define LM49453_P0_PORT2_TX1_REG 0x4C
+#define LM49453_P0_PORT2_TX2_REG 0x4D
+#define LM49453_P0_STN_SEL_REG 0x4F
+#define LM49453_P0_DACHPL1_REG 0x50
+#define LM49453_P0_DACHPL2_REG 0x51
+#define LM49453_P0_DACHPR1_REG 0x52
+#define LM49453_P0_DACHPR2_REG 0x53
+#define LM49453_P0_DACLOL1_REG 0x54
+#define LM49453_P0_DACLOL2_REG 0x55
+#define LM49453_P0_DACLOR1_REG 0x56
+#define LM49453_P0_DACLOR2_REG 0x57
+#define LM49453_P0_DACLSL1_REG 0x58
+#define LM49453_P0_DACLSL2_REG 0x59
+#define LM49453_P0_DACLSR1_REG 0x5A
+#define LM49453_P0_DACLSR2_REG 0x5B
+#define LM49453_P0_DACHAL1_REG 0x5C
+#define LM49453_P0_DACHAL2_REG 0x5D
+#define LM49453_P0_DACHAR1_REG 0x5E
+#define LM49453_P0_DACHAR2_REG 0x5F
+
+/* AUDIO PORT 1 (TDM) */
+#define LM49453_P0_AUDIO_PORT1_BASIC_REG 0x60
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG 0x61
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG 0x62
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG 0x63
+#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG 0x64
+#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG 0x65
+#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG 0x66
+#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG 0x67
+#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG 0x68
+#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG 0x69
+
+/* AUDIO PORT 2 */
+#define LM49453_P0_AUDIO_PORT2_BASIC_REG 0x6A
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG 0x6B
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG 0x6C
+#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG 0x6D
+#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG 0x6E
+#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG 0x6F
+#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG 0x70
+
+/* SAMPLE RATE */
+#define LM49453_P0_PORT1_SR_LSB_REG 0x79
+#define LM49453_P0_PORT1_SR_MSB_REG 0x7A
+#define LM49453_P0_PORT2_SR_LSB_REG 0x7B
+#define LM49453_P0_PORT2_SR_MSB_REG 0x7C
+
+/* EFFECTS - HPFs */
+#define LM49453_P0_HPF_REG 0x80
+
+/* EFFECTS ADC ALC */
+#define LM49453_P0_ADC_ALC1_REG 0x82
+#define LM49453_P0_ADC_ALC2_REG 0x83
+#define LM49453_P0_ADC_ALC3_REG 0x84
+#define LM49453_P0_ADC_ALC4_REG 0x85
+#define LM49453_P0_ADC_ALC5_REG 0x86
+#define LM49453_P0_ADC_ALC6_REG 0x87
+#define LM49453_P0_ADC_ALC7_REG 0x88
+#define LM49453_P0_ADC_ALC8_REG 0x89
+#define LM49453_P0_DMIC1_LEVELL_REG 0x8A
+#define LM49453_P0_DMIC1_LEVELR_REG 0x8B
+#define LM49453_P0_DMIC2_LEVELL_REG 0x8C
+#define LM49453_P0_DMIC2_LEVELR_REG 0x8D
+#define LM49453_P0_ADC_LEVELL_REG 0x8E
+#define LM49453_P0_ADC_LEVELR_REG 0x8F
+#define LM49453_P0_DAC_HP_LEVELL_REG 0x90
+#define LM49453_P0_DAC_HP_LEVELR_REG 0x91
+#define LM49453_P0_DAC_LO_LEVELL_REG 0x92
+#define LM49453_P0_DAC_LO_LEVELR_REG 0x93
+#define LM49453_P0_DAC_LS_LEVELL_REG 0x94
+#define LM49453_P0_DAC_LS_LEVELR_REG 0x95
+#define LM49453_P0_DAC_HA_LEVELL_REG 0x96
+#define LM49453_P0_DAC_HA_LEVELR_REG 0x97
+#define LM49453_P0_SOFT_MUTE_REG 0x98
+#define LM49453_P0_DMIC_MUTE_CFG_REG 0x99
+#define LM49453_P0_ADC_MUTE_CFG_REG 0x9A
+#define LM49453_P0_DAC_MUTE_CFG_REG 0x9B
+
+/*DIGITAL MIC1 */
+#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG 0xB0
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG 0xB1
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG 0xB2
+
+/*DIGITAL MIC2 */
+#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG 0xB3
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG 0xB4
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG 0xB5
+
+/* ADC DECIMATOR */
+#define LM49453_P0_ADC_DECIMATOR_REG 0xB6
+
+/* DAC CONFIGURE */
+#define LM49453_P0_DAC_CONFIG_REG 0xB7
+
+/* SIDETONE */
+#define LM49453_P0_STN_VOL_ADCL_REG 0xB8
+#define LM49453_P0_STN_VOL_ADCR_REG 0xB9
+#define LM49453_P0_STN_VOL_DMIC1L_REG 0xBA
+#define LM49453_P0_STN_VOL_DMIC1R_REG 0xBB
+#define LM49453_P0_STN_VOL_DMIC2L_REG 0xBC
+#define LM49453_P0_STN_VOL_DMIC2R_REG 0xBD
+
+/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */
+#define LM49453_P0_ADC_DEC_CLIP_REG 0xC2
+#define LM49453_P0_ADC_HPF_CLIP_REG 0xC3
+#define LM49453_P0_ADC_LVL_CLIP_REG 0xC4
+#define LM49453_P0_DAC_LVL_CLIP_REG 0xC5
+
+/* ADC ALC EFFECT MONITORS (Read Only) */
+#define LM49453_P0_ADC_LVLMONL_REG 0xC8
+#define LM49453_P0_ADC_LVLMONR_REG 0xC9
+#define LM49453_P0_ADC_ALCMONL_REG 0xCA
+#define LM49453_P0_ADC_ALCMONR_REG 0xCB
+#define LM49453_P0_ADC_MUTED_REG 0xCC
+#define LM49453_P0_DAC_MUTED_REG 0xCD
+
+/* HEADSET DETECT */
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG 0xD0
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG 0xD1
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG 0xD2
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG 0xD3
+#define LM49453_P0_HSD_TIMEOUT1_REG 0xD4
+#define LM49453_P0_HSD_TIMEOUT2_REG 0xD5
+#define LM49453_P0_HSD_TIMEOUT3_REG 0xD6
+#define LM49453_P0_HSD_PIN3_4_CFG_REG 0xD7
+#define LM49453_P0_HSD_IRQ1_REG 0xD8
+#define LM49453_P0_HSD_IRQ2_REG 0xD9
+#define LM49453_P0_HSD_IRQ3_REG 0xDA
+#define LM49453_P0_HSD_IRQ4_REG 0xDB
+#define LM49453_P0_HSD_IRQ_MASK1_REG 0xDC
+#define LM49453_P0_HSD_IRQ_MASK2_REG 0xDD
+#define LM49453_P0_HSD_IRQ_MASK3_REG 0xDE
+#define LM49453_P0_HSD_R_HPLL_REG 0xE0
+#define LM49453_P0_HSD_R_HPLH_REG 0xE1
+#define LM49453_P0_HSD_R_HPLU_REG 0xE2
+#define LM49453_P0_HSD_R_HPRL_REG 0xE3
+#define LM49453_P0_HSD_R_HPRH_REG 0xE4
+#define LM49453_P0_HSD_R_HPRU_REG 0xE5
+#define LM49453_P0_HSD_VEL_L_FINALL_REG 0xE6
+#define LM49453_P0_HSD_VEL_L_FINALH_REG 0xE7
+#define LM49453_P0_HSD_VEL_L_FINALU_REG 0xE8
+#define LM49453_P0_HSD_RO_FINALL_REG 0xE9
+#define LM49453_P0_HSD_RO_FINALH_REG 0xEA
+#define LM49453_P0_HSD_RO_FINALU_REG 0xEB
+#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG 0xEC
+#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG 0xED
+#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG 0xEE
+#define LM49453_P0_HSD_PIN_CONFIG_REG 0xEF
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG 0xF1
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG 0xF2
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG 0xF3
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG 0xF4
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG 0xF5
+
+/* I/O PULLDOWN CONFIG */
+#define LM49453_P0_PULL_CONFIG1_REG 0xF8
+#define LM49453_P0_PULL_CONFIG2_REG 0xF9
+#define LM49453_P0_PULL_CONFIG3_REG 0xFA
+
+/* RESET */
+#define LM49453_P0_RESET_REG 0xFE
+
+/* PAGE */
+#define LM49453_PAGE_REG 0xFF
+
+#define LM49453_MAX_REGISTER (0xFF+1)
+
+/* LM49453_P0_PMC_SETUP_REG (0x00h) */
+#define LM49453_PMC_SETUP_CHIP_EN (BIT(1)|BIT(0))
+#define LM49453_PMC_SETUP_PLL_EN BIT(2)
+#define LM49453_PMC_SETUP_PLL_P2_EN BIT(3)
+#define LM49453_PMC_SETUP_PLL_FLL BIT(4)
+#define LM49453_PMC_SETUP_MCLK_OVER BIT(5)
+#define LM49453_PMC_SETUP_RTC_CLK_OVER BIT(6)
+#define LM49453_PMC_SETUP_CHIP_ACTIVE BIT(7)
+
+/* Chip Enable bits */
+#define LM49453_CHIP_EN_SHUTDOWN 0x00
+#define LM49453_CHIP_EN 0x01
+#define LM49453_CHIP_EN_HSD_DETECT 0x02
+#define LM49453_CHIP_EN_INVALID_HSD 0x03
+
+/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */
+#define LM49453_CLK_SEL1_MCLK_SEL 0x11
+#define LM49453_CLK_SEL1_RTC_SEL 0x11
+#define LM49453_CLK_SEL1_PORT1_SEL 0x10
+#define LM49453_CLK_SEL1_PORT2_SEL 0x11
+
+/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */
+#define LM49453_CLK_SEL2_ADC_CLK_SEL 0x38
+
+/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */
+#define LM49453_FLL_REF_FREQ_VAL 0x8ca0001
+
+/* LM49453_P0_VCO_TARGETLL_REG (0x11) */
+#define LM49453_VCO_TARGET_VAL 0x8ca0001
+
+/* LM49453_P0_ADC_DSP_REG (0x30h) */
+#define LM49453_ADC_DSP_ADC_MUTEL BIT(0)
+#define LM49453_ADC_DSP_ADC_MUTER BIT(1)
+#define LM49453_ADC_DSP_DMIC1_MUTEL BIT(2)
+#define LM49453_ADC_DSP_DMIC1_MUTER BIT(3)
+#define LM49453_ADC_DSP_DMIC2_MUTEL BIT(4)
+#define LM49453_ADC_DSP_DMIC2_MUTER BIT(5)
+#define LM49453_ADC_DSP_MUTE_ALL 0x3F
+
+/* LM49453_P0_DAC_DSP_REG (0x31h) */
+#define LM49453_DAC_DSP_MUTE_ALL 0xFF
+
+/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */
+#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK (BIT(4)|BIT(3))
+#define LM49453_AUDIO_PORT1_BASIC_CLK_MS BIT(3)
+#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS BIT(4)
+
+/* LM49453_P0_RESET_REG (0xFEh) */
+#define LM49453_RESET_REG_RST BIT(0)
+
+/* Page select register bits (0xFF) */
+#define LM49453_PAGE0_SELECT 0x0
+#define LM49453_PAGE1_SELECT 0x1
+
+/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */
+#define LM49453_JACK_DISABLE 0x00
+#define LM49453_JACK_CONFIG1 0x01
+#define LM49453_JACK_CONFIG2 0x02
+#define LM49453_JACK_CONFIG3 0x03
+#define LM49453_JACK_CONFIG4 0x04
+#define LM49453_JACK_CONFIG5 0x05
+
+/* Page 1 REGISTERS */
+
+/* SIDETONE */
+#define LM49453_P1_SIDETONE_SA0L_REG 0x80
+#define LM49453_P1_SIDETONE_SA0H_REG 0x81
+#define LM49453_P1_SIDETONE_SAB0U_REG 0x82
+#define LM49453_P1_SIDETONE_SB0L_REG 0x83
+#define LM49453_P1_SIDETONE_SB0H_REG 0x84
+#define LM49453_P1_SIDETONE_SH0L_REG 0x85
+#define LM49453_P1_SIDETONE_SH0H_REG 0x86
+#define LM49453_P1_SIDETONE_SH0U_REG 0x87
+#define LM49453_P1_SIDETONE_SA1L_REG 0x88
+#define LM49453_P1_SIDETONE_SA1H_REG 0x89
+#define LM49453_P1_SIDETONE_SAB1U_REG 0x8A
+#define LM49453_P1_SIDETONE_SB1L_REG 0x8B
+#define LM49453_P1_SIDETONE_SB1H_REG 0x8C
+#define LM49453_P1_SIDETONE_SH1L_REG 0x8D
+#define LM49453_P1_SIDETONE_SH1H_REG 0x8E
+#define LM49453_P1_SIDETONE_SH1U_REG 0x8F
+#define LM49453_P1_SIDETONE_SA2L_REG 0x90
+#define LM49453_P1_SIDETONE_SA2H_REG 0x91
+#define LM49453_P1_SIDETONE_SAB2U_REG 0x92
+#define LM49453_P1_SIDETONE_SB2L_REG 0x93
+#define LM49453_P1_SIDETONE_SB2H_REG 0x94
+#define LM49453_P1_SIDETONE_SH2L_REG 0x95
+#define LM49453_P1_SIDETONE_SH2H_REG 0x96
+#define LM49453_P1_SIDETONE_SH2U_REG 0x97
+#define LM49453_P1_SIDETONE_SA3L_REG 0x98
+#define LM49453_P1_SIDETONE_SA3H_REG 0x99
+#define LM49453_P1_SIDETONE_SAB3U_REG 0x9A
+#define LM49453_P1_SIDETONE_SB3L_REG 0x9B
+#define LM49453_P1_SIDETONE_SB3H_REG 0x9C
+#define LM49453_P1_SIDETONE_SH3L_REG 0x9D
+#define LM49453_P1_SIDETONE_SH3H_REG 0x9E
+#define LM49453_P1_SIDETONE_SH3U_REG 0x9F
+#define LM49453_P1_SIDETONE_SA4L_REG 0xA0
+#define LM49453_P1_SIDETONE_SA4H_REG 0xA1
+#define LM49453_P1_SIDETONE_SAB4U_REG 0xA2
+#define LM49453_P1_SIDETONE_SB4L_REG 0xA3
+#define LM49453_P1_SIDETONE_SB4H_REG 0xA4
+#define LM49453_P1_SIDETONE_SH4L_REG 0xA5
+#define LM49453_P1_SIDETONE_SH4H_REG 0xA6
+#define LM49453_P1_SIDETONE_SH4U_REG 0xA7
+#define LM49453_P1_SIDETONE_SA5L_REG 0xA8
+#define LM49453_P1_SIDETONE_SA5H_REG 0xA9
+#define LM49453_P1_SIDETONE_SAB5U_REG 0xAA
+#define LM49453_P1_SIDETONE_SB5L_REG 0xAB
+#define LM49453_P1_SIDETONE_SB5H_REG 0xAC
+#define LM49453_P1_SIDETONE_SH5L_REG 0xAD
+#define LM49453_P1_SIDETONE_SH5H_REG 0xAE
+#define LM49453_P1_SIDETONE_SH5U_REG 0xAF
+
+/* CHARGE PUMP CONFIG */
+#define LM49453_P1_CP_CONFIG1_REG 0xB0
+#define LM49453_P1_CP_CONFIG2_REG 0xB1
+#define LM49453_P1_CP_CONFIG3_REG 0xB2
+#define LM49453_P1_CP_CONFIG4_REG 0xB3
+#define LM49453_P1_CP_LA_VTH1L_REG 0xB4
+#define LM49453_P1_CP_LA_VTH1M_REG 0xB5
+#define LM49453_P1_CP_LA_VTH2L_REG 0xB6
+#define LM49453_P1_CP_LA_VTH2M_REG 0xB7
+#define LM49453_P1_CP_LA_VTH3L_REG 0xB8
+#define LM49453_P1_CP_LA_VTH3H_REG 0xB9
+#define LM49453_P1_CP_CLK_DIV_REG 0xBA
+
+/* DAC */
+#define LM49453_P1_DAC_CHOP_REG 0xC0
+
+#define LM49453_CLK_SRC_MCLK 1
+#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 0bb511a0388..35179e2c23c 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <asm/div64.h>
#include <sound/max98095.h>
+#include <sound/jack.h>
#include "max98095.h"
enum max98095_type {
@@ -51,6 +52,8 @@ struct max98095_priv {
u8 lin_state;
unsigned int mic1pre;
unsigned int mic2pre;
+ struct snd_soc_jack *headphone_jack;
+ struct snd_soc_jack *mic_jack;
};
static const u8 max98095_reg_def[M98095_REG_CNT] = {
@@ -2173,9 +2176,125 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec)
max98095_handle_bq_pdata(codec);
}
+static irqreturn_t max98095_report_jack(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+ int hp_report = 0;
+ int mic_report = 0;
+
+ /* Read the Jack Status Register */
+ value = snd_soc_read(codec, M98095_007_JACK_AUTO_STS);
+
+ /* If ddone is not set, then detection isn't finished yet */
+ if ((value & M98095_DDONE) == 0)
+ return IRQ_NONE;
+
+ /* if hp, check its bit, and if set, clear it */
+ if ((value & M98095_HP_IN || value & M98095_LO_IN) &&
+ max98095->headphone_jack)
+ hp_report |= SND_JACK_HEADPHONE;
+
+ /* if mic, check its bit, and if set, clear it */
+ if ((value & M98095_MIC_IN) && max98095->mic_jack)
+ mic_report |= SND_JACK_MICROPHONE;
+
+ if (max98095->headphone_jack == max98095->mic_jack) {
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report | mic_report,
+ SND_JACK_HEADSET);
+ } else {
+ if (max98095->headphone_jack)
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report, SND_JACK_HEADPHONE);
+ if (max98095->mic_jack)
+ snd_soc_jack_report(max98095->mic_jack,
+ mic_report, SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ int detect_enable = M98095_JDEN;
+ unsigned int slew = M98095_DEFAULT_SLEW_DELAY;
+
+ if (max98095->pdata->jack_detect_pin5en)
+ detect_enable |= M98095_PIN5EN;
+
+ if (max98095->pdata->jack_detect_delay)
+ slew = max98095->pdata->jack_detect_delay;
+
+ ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ /* configure auto detection to be enabled */
+ ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int max98095_jack_detect_disable(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ /* configure auto detection to be disabled */
+ ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int max98095_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+ int ret = 0;
+
+ max98095->headphone_jack = hp_jack;
+ max98095->mic_jack = mic_jack;
+
+ /* only progress if we have at least 1 jack pointer */
+ if (!hp_jack && !mic_jack)
+ return -EINVAL;
+
+ max98095_jack_detect_enable(codec);
+
+ /* enable interrupts for headphone jack detection */
+ ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+ M98095_IDDONE, M98095_IDDONE);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+ return ret;
+ }
+
+ max98095_report_jack(client->irq, codec);
+ return 0;
+}
+
#ifdef CONFIG_PM
static int max98095_suspend(struct snd_soc_codec *codec)
{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(codec);
+
max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -2183,8 +2302,16 @@ static int max98095_suspend(struct snd_soc_codec *codec)
static int max98095_resume(struct snd_soc_codec *codec)
{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+
max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (max98095->headphone_jack || max98095->mic_jack) {
+ max98095_jack_detect_enable(codec);
+ max98095_report_jack(client->irq, codec);
+ }
+
return 0;
}
#else
@@ -2227,6 +2354,7 @@ static int max98095_probe(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct max98095_cdata *cdata;
+ struct i2c_client *client;
int ret = 0;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
@@ -2238,6 +2366,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
/* reset the codec, the DSP core, and disable all interrupts */
max98095_reset(codec);
+ client = to_i2c_client(codec->dev);
+
/* initialize private data */
max98095->sysclk = (unsigned)-1;
@@ -2266,11 +2396,23 @@ static int max98095_probe(struct snd_soc_codec *codec)
max98095->mic1pre = 0;
max98095->mic2pre = 0;
+ if (client->irq) {
+ /* register an audio interrupt */
+ ret = request_threaded_irq(client->irq, NULL,
+ max98095_report_jack,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "max98095", codec);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ goto err_access;
+ }
+ }
+
ret = snd_soc_read(codec, M98095_0FF_REV_ID);
if (ret < 0) {
dev_err(codec->dev, "Failure reading hardware revision: %d\n",
ret);
- goto err_access;
+ goto err_irq;
}
dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
@@ -2306,14 +2448,28 @@ static int max98095_probe(struct snd_soc_codec *codec)
max98095_add_widgets(codec);
+ return 0;
+
+err_irq:
+ if (client->irq)
+ free_irq(client->irq, codec);
err_access:
return ret;
}
static int max98095_remove(struct snd_soc_codec *codec)
{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+
max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(codec);
+
+ if (client->irq)
+ free_irq(client->irq, codec);
+
return 0;
}
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
index 891584a0eb0..2ebbe4e894b 100644
--- a/sound/soc/codecs/max98095.h
+++ b/sound/soc/codecs/max98095.h
@@ -175,11 +175,23 @@
/* MAX98095 Registers Bit Fields */
+/* M98095_007_JACK_AUTO_STS */
+ #define M98095_MIC_IN (1<<3)
+ #define M98095_LO_IN (1<<5)
+ #define M98095_HP_IN (1<<6)
+ #define M98095_DDONE (1<<7)
+
/* M98095_00F_HOST_CFG */
#define M98095_SEG (1<<0)
#define M98095_XTEN (1<<1)
#define M98095_MDLLEN (1<<2)
+/* M98095_013_JACK_INT_EN */
+ #define M98095_IMIC_IN (1<<3)
+ #define M98095_ILO_IN (1<<5)
+ #define M98095_IHP_IN (1<<6)
+ #define M98095_IDDONE (1<<7)
+
/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
#define M98095_CLKMODE_MASK 0xFF
@@ -255,6 +267,10 @@
#define M98095_EQ2EN (1<<1)
#define M98095_EQ1EN (1<<0)
+/* M98095_089_JACK_DET_AUTO */
+ #define M98095_PIN5EN (1<<2)
+ #define M98095_JDEN (1<<7)
+
/* M98095_090_PWR_EN_IN */
#define M98095_INEN (1<<7)
#define M98095_MB2EN (1<<3)
@@ -296,4 +312,10 @@
#define M98095_174_DAI1_BQ_BASE 0x74
#define M98095_17E_DAI2_BQ_BASE 0x7E
+/* Default Delay used in Slew Rate Calculation for Jack detection */
+#define M98095_DEFAULT_SLEW_DELAY 0x18
+
+extern int max98095_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+
#endif
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
new file mode 100644
index 00000000000..6276e352125
--- /dev/null
+++ b/sound/soc/codecs/mc13783.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de
+ * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+
+#include "mc13783.h"
+
+#define MC13783_AUDIO_RX0 36
+#define MC13783_AUDIO_RX1 37
+#define MC13783_AUDIO_TX 38
+#define MC13783_SSI_NETWORK 39
+#define MC13783_AUDIO_CODEC 40
+#define MC13783_AUDIO_DAC 41
+
+#define AUDIO_RX0_ALSPEN (1 << 5)
+#define AUDIO_RX0_ALSPSEL (1 << 7)
+#define AUDIO_RX0_ADDCDC (1 << 21)
+#define AUDIO_RX0_ADDSTDC (1 << 22)
+#define AUDIO_RX0_ADDRXIN (1 << 23)
+
+#define AUDIO_RX1_PGARXEN (1 << 0);
+#define AUDIO_RX1_PGASTEN (1 << 5)
+#define AUDIO_RX1_ARXINEN (1 << 10)
+
+#define AUDIO_TX_AMC1REN (1 << 5)
+#define AUDIO_TX_AMC1LEN (1 << 7)
+#define AUDIO_TX_AMC2EN (1 << 9)
+#define AUDIO_TX_ATXINEN (1 << 11)
+#define AUDIO_TX_RXINREC (1 << 13)
+
+#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2)
+#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4)
+#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6)
+#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8)
+#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10)
+#define SSI_NETWORK_CDCFSDLY(x) (1 << 11)
+#define SSI_NETWORK_DAC_SLOTS_8 (1 << 12)
+#define SSI_NETWORK_DAC_SLOTS_4 (2 << 12)
+#define SSI_NETWORK_DAC_SLOTS_2 (3 << 12)
+#define SSI_NETWORK_DAC_SLOT_MASK (3 << 12)
+#define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14)
+#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16)
+#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18)
+#define SSI_NETWORK_STDCSUMGAIN (1 << 20)
+
+/*
+ * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same
+ * register layout
+ */
+#define AUDIO_SSI_SEL (1 << 0)
+#define AUDIO_CLK_SEL (1 << 1)
+#define AUDIO_CSM (1 << 2)
+#define AUDIO_BCL_INV (1 << 3)
+#define AUDIO_CFS_INV (1 << 4)
+#define AUDIO_CFS(x) (((x) & 0x3) << 5)
+#define AUDIO_CLK(x) (((x) & 0x7) << 7)
+#define AUDIO_C_EN (1 << 11)
+#define AUDIO_C_CLK_EN (1 << 12)
+#define AUDIO_C_RESET (1 << 15)
+
+#define AUDIO_CODEC_CDCFS8K16K (1 << 10)
+#define AUDIO_DAC_CFS_DLY_B (1 << 10)
+
+struct mc13783_priv {
+ struct snd_soc_codec codec;
+ struct mc13xxx *mc13xxx;
+
+ enum mc13783_ssi_port adc_ssi_port;
+ enum mc13783_ssi_port dac_ssi_port;
+};
+
+static unsigned int mc13783_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int value = 0;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ mc13xxx_reg_read(priv->mc13xxx, reg, &value);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return value;
+}
+
+static int mc13783_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+/* Mapping between sample rates and register value */
+static unsigned int mc13783_rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 96000
+};
+
+static int mc13783_pcm_hw_params_dac(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;
+ unsigned int rate = params_rate(params);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) {
+ if (rate == mc13783_rates[i]) {
+ snd_soc_update_bits(codec, MC13783_AUDIO_DAC,
+ 0xf << 17, i << 17);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mc13783_pcm_hw_params_codec(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;
+ unsigned int rate = params_rate(params);
+ unsigned int val;
+
+ switch (rate) {
+ case 8000:
+ val = 0;
+ break;
+ case 16000:
+ val = AUDIO_CODEC_CDCFS8K16K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ val);
+
+ return 0;
+}
+
+static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return mc13783_pcm_hw_params_dac(substream, params, dai);
+ else
+ return mc13783_pcm_hw_params_codec(substream, params, dai);
+}
+
+static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int reg)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
+ AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
+
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= AUDIO_CFS(2);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= AUDIO_CFS(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val |= AUDIO_BCL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= AUDIO_BCL_INV | AUDIO_CFS_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= AUDIO_CFS_INV;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ val |= AUDIO_C_CLK_EN;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ val |= AUDIO_CSM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ return -EINVAL;
+ }
+
+ val |= AUDIO_C_RESET;
+
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ if (dai->id == MC13783_ID_STEREO_DAC)
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ else
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int ret;
+
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ /*
+ * In synchronous mode force the voice codec into slave mode
+ * so that the clock / framesync from the stereo DAC is used
+ */
+ fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+
+ return ret;
+}
+
+static int mc13783_sysclk[] = {
+ 13000000,
+ 15360000,
+ 16800000,
+ -1,
+ 26000000,
+ -1, /* 12000000, invalid for voice codec */
+ -1, /* 3686400, invalid for voice codec */
+ 33600000,
+};
+
+static int mc13783_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir,
+ unsigned int reg)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int clk;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
+
+ for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) {
+ if (mc13783_sysclk[clk] < 0)
+ continue;
+ if (mc13783_sysclk[clk] == freq)
+ break;
+ }
+
+ if (clk == ARRAY_SIZE(mc13783_sysclk))
+ return -EINVAL;
+
+ if (clk_id == MC13783_CLK_CLIB)
+ val |= AUDIO_CLK_SEL;
+
+ val |= AUDIO_CLK(clk);
+
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+}
+
+static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ int ret;
+
+ ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
+ SSI_NETWORK_DAC_RXSLOT_MASK;
+
+ switch (slots) {
+ case 2:
+ val |= SSI_NETWORK_DAC_SLOTS_2;
+ break;
+ case 4:
+ val |= SSI_NETWORK_DAC_SLOTS_4;
+ break;
+ case 8:
+ val |= SSI_NETWORK_DAC_SLOTS_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rx_mask) {
+ case 0xfffffffc:
+ val |= SSI_NETWORK_DAC_RXSLOT_0_1;
+ break;
+ case 0xfffffff3:
+ val |= SSI_NETWORK_DAC_RXSLOT_2_3;
+ break;
+ case 0xffffffcf:
+ val |= SSI_NETWORK_DAC_RXSLOT_4_5;
+ break;
+ case 0xffffff3f:
+ val |= SSI_NETWORK_DAC_RXSLOT_6_7;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = 0x3f;
+
+ if (slots != 4)
+ return -EINVAL;
+
+ if (tx_mask != 0xfffffffc)
+ return -EINVAL;
+
+ val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
+ val |= (0x01 << 4); /* secondary timeslot TX is 1 */
+
+ snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ int ret;
+
+ ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots,
+ slot_width);
+ if (ret)
+ return ret;
+
+ ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots,
+ slot_width);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new mc1l_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", 38, 7, 1, 0);
+
+static const struct snd_kcontrol_new mc1r_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", 38, 5, 1, 0);
+
+static const struct snd_kcontrol_new mc2_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", 38, 9, 1, 0);
+
+static const struct snd_kcontrol_new atx_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", 38, 11, 1, 0);
+
+
+/* Virtual mux. The chip does the input selection automatically
+ * as soon as we enable one input. */
+static const char * const adcl_enum_text[] = {
+ "MC1L", "RXINL",
+};
+
+static const struct soc_enum adcl_enum =
+ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text);
+
+static const struct snd_kcontrol_new left_input_mux =
+ SOC_DAPM_ENUM_VIRT("Route", adcl_enum);
+
+static const char * const adcr_enum_text[] = {
+ "MC1R", "MC2", "RXINR", "TXIN",
+};
+
+static const struct soc_enum adcr_enum =
+ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text);
+
+static const struct snd_kcontrol_new right_input_mux =
+ SOC_DAPM_ENUM_VIRT("Route", adcr_enum);
+
+static const struct snd_kcontrol_new samp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 3, 1, 0);
+
+static const struct snd_kcontrol_new lamp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 5, 1, 0);
+
+static const struct snd_kcontrol_new hlamp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 10, 1, 0);
+
+static const struct snd_kcontrol_new hramp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 9, 1, 0);
+
+static const struct snd_kcontrol_new llamp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 16, 1, 0);
+
+static const struct snd_kcontrol_new lramp_ctl =
+ SOC_DAPM_SINGLE("Switch", 36, 15, 1, 0);
+
+static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
+/* Input */
+ SND_SOC_DAPM_INPUT("MC1LIN"),
+ SND_SOC_DAPM_INPUT("MC1RIN"),
+ SND_SOC_DAPM_INPUT("MC2IN"),
+ SND_SOC_DAPM_INPUT("RXINR"),
+ SND_SOC_DAPM_INPUT("RXINL"),
+ SND_SOC_DAPM_INPUT("TXIN"),
+
+ SND_SOC_DAPM_SUPPLY("MC1 Bias", 38, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MC2 Bias", 38, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("MC1L Amp", 38, 7, 0, &mc1l_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC1R Amp", 38, 5, 0, &mc1r_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC2 Amp", 38, 9, 0, &mc2_amp_ctl),
+ SND_SOC_DAPM_SWITCH("TXIN Amp", 38, 11, 0, &atx_amp_ctl),
+
+ SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
+ &left_input_mux),
+ SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
+ &right_input_mux),
+
+ SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", 40, 11, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_Reset", 40, 15, 0, NULL, 0),
+
+/* Output */
+ SND_SOC_DAPM_SUPPLY("DAC_E", 41, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_Reset", 41, 15, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("RXOUTL"),
+ SND_SOC_DAPM_OUTPUT("RXOUTR"),
+ SND_SOC_DAPM_OUTPUT("HSL"),
+ SND_SOC_DAPM_OUTPUT("HSR"),
+ SND_SOC_DAPM_OUTPUT("LSP"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+
+ SND_SOC_DAPM_SWITCH("Speaker Amp", 36, 3, 0, &samp_ctl),
+ SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Left", 36, 10, 0, &hlamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Right", 36, 9, 0, &hramp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Left", 36, 16, 0, &llamp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Right", 36, 15, 0, &lramp_ctl),
+ SND_SOC_DAPM_DAC("DAC", "Playback", 36, 22, 0),
+ SND_SOC_DAPM_PGA("DAC PGA", 37, 5, 0, NULL, 0),
+};
+
+static struct snd_soc_dapm_route mc13783_routes[] = {
+/* Input */
+ { "MC1L Amp", NULL, "MC1LIN"},
+ { "MC1R Amp", NULL, "MC1RIN" },
+ { "MC2 Amp", NULL, "MC2IN" },
+ { "TXIN Amp", NULL, "TXIN"},
+
+ { "PGA Left Input Mux", "MC1L", "MC1L Amp" },
+ { "PGA Left Input Mux", "RXINL", "RXINL"},
+ { "PGA Right Input Mux", "MC1R", "MC1R Amp" },
+ { "PGA Right Input Mux", "MC2", "MC2 Amp"},
+ { "PGA Right Input Mux", "TXIN", "TXIN Amp"},
+ { "PGA Right Input Mux", "RXINR", "RXINR"},
+
+ { "PGA Left Input", NULL, "PGA Left Input Mux"},
+ { "PGA Right Input", NULL, "PGA Right Input Mux"},
+
+ { "ADC", NULL, "PGA Left Input"},
+ { "ADC", NULL, "PGA Right Input"},
+ { "ADC", NULL, "ADC_Reset"},
+
+/* Output */
+ { "HSL", NULL, "Headset Amp Left" },
+ { "HSR", NULL, "Headset Amp Right"},
+ { "RXOUTL", NULL, "Line out Amp Left"},
+ { "RXOUTR", NULL, "Line out Amp Right"},
+ { "SP", NULL, "Speaker Amp"},
+ { "Speaker Amp", NULL, "DAC PGA"},
+ { "LSP", NULL, "DAC PGA"},
+ { "Headset Amp Left", NULL, "DAC PGA"},
+ { "Headset Amp Right", NULL, "DAC PGA"},
+ { "Line out Amp Left", NULL, "DAC PGA"},
+ { "Line out Amp Right", NULL, "DAC PGA"},
+ { "DAC PGA", NULL, "DAC"},
+ { "DAC", NULL, "DAC_E"},
+};
+
+static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
+ "Mono", "Mono Mix"};
+
+static const struct soc_enum mc13783_enum_3d_mixer =
+ SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer),
+ mc13783_3d_mixer);
+
+static struct snd_kcontrol_new mc13783_control_list[] = {
+ SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
+ SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0),
+ SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0),
+ SOC_ENUM("3D Control", mc13783_enum_3d_mixer),
+};
+
+static int mc13783_probe(struct snd_soc_codec *codec)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /* these are the reset values */
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004);
+
+ if (priv->adc_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ 0, AUDIO_SSI_SEL);
+
+ if (priv->dac_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ 0, AUDIO_SSI_SEL);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return 0;
+}
+
+static int mc13783_remove(struct snd_soc_codec *codec)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /* Make sure VAUDIOON is off */
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return 0;
+}
+
+#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops mc13783_ops_dac = {
+ .hw_params = mc13783_pcm_hw_params_dac,
+ .set_fmt = mc13783_set_fmt_async,
+ .set_sysclk = mc13783_set_sysclk_dac,
+ .set_tdm_slot = mc13783_set_tdm_slot_dac,
+};
+
+static struct snd_soc_dai_ops mc13783_ops_codec = {
+ .hw_params = mc13783_pcm_hw_params_codec,
+ .set_fmt = mc13783_set_fmt_async,
+ .set_sysclk = mc13783_set_sysclk_codec,
+ .set_tdm_slot = mc13783_set_tdm_slot_codec,
+};
+
+/*
+ * The mc13783 has two SSI ports, both of them can be routed either
+ * to the voice codec or the stereo DAC. When two different SSI ports
+ * are used for the voice codec and the stereo DAC we can do different
+ * formats and sysclock settings for playback and capture
+ * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port
+ * forces us to use symmetric rates (mc13783-hifi).
+ */
+static struct snd_soc_dai_driver mc13783_dai_async[] = {
+ {
+ .name = "mc13783-hifi-playback",
+ .id = MC13783_ID_STEREO_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_dac,
+ }, {
+ .name = "mc13783-hifi-capture",
+ .id = MC13783_ID_STEREO_CODEC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_codec,
+ },
+};
+
+static struct snd_soc_dai_ops mc13783_ops_sync = {
+ .hw_params = mc13783_pcm_hw_params_sync,
+ .set_fmt = mc13783_set_fmt_sync,
+ .set_sysclk = mc13783_set_sysclk_sync,
+ .set_tdm_slot = mc13783_set_tdm_slot_sync,
+};
+
+static struct snd_soc_dai_driver mc13783_dai_sync[] = {
+ {
+ .name = "mc13783-hifi",
+ .id = MC13783_ID_SYNC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_sync,
+ .symmetric_rates = 1,
+ }
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
+ .probe = mc13783_probe,
+ .remove = mc13783_remove,
+ .read = mc13783_read,
+ .write = mc13783_write,
+ .controls = mc13783_control_list,
+ .num_controls = ARRAY_SIZE(mc13783_control_list),
+ .dapm_widgets = mc13783_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
+ .dapm_routes = mc13783_routes,
+ .num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+};
+
+static int mc13783_codec_probe(struct platform_device *pdev)
+{
+ struct mc13xxx *mc13xxx;
+ struct mc13783_priv *priv;
+ struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ mc13xxx = dev_get_drvdata(pdev->dev.parent);
+
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->mc13xxx = mc13xxx;
+ if (pdata) {
+ priv->adc_ssi_port = pdata->adc_ssi_port;
+ priv->dac_ssi_port = pdata->dac_ssi_port;
+ } else {
+ priv->adc_ssi_port = MC13783_SSI1_PORT;
+ priv->dac_ssi_port = MC13783_SSI2_PORT;
+ }
+
+ if (priv->adc_ssi_port == priv->dac_ssi_port)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
+ else
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
+
+ if (ret)
+ goto err_register_codec;
+
+ return 0;
+
+err_register_codec:
+ dev_err(&pdev->dev, "register codec failed with %d\n", ret);
+
+ return ret;
+}
+
+static int mc13783_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_codec_driver = {
+ .driver = {
+ .name = "mc13783-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = mc13783_codec_probe,
+ .remove = __devexit_p(mc13783_codec_remove),
+};
+
+module_platform_driver(mc13783_codec_driver);
+
+MODULE_DESCRIPTION("ASoC MC13783 driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
new file mode 100644
index 00000000000..3a6d1993a21
--- /dev/null
+++ b/sound/soc/codecs/mc13783.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MC13783_MIXER_H
+#define MC13783_MIXER_H
+
+#define MC13783_CLK_CLIA 1
+#define MC13783_CLK_CLIB 2
+
+#define MC13783_ID_STEREO_DAC 1
+#define MC13783_ID_STEREO_CODEC 2
+#define MC13783_ID_SYNC 3
+
+#endif /* MC13783_MIXER_H */
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 00000000000..22cb5bf5927
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY BIT(1)
+#define ML26124_SAI_FRAME_SYNC (BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 212
+#define ML26124_VMID BIT(1)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+ u32 mclk;
+ u32 rate;
+ struct regmap *regmap;
+ int clk_in;
+ struct snd_pcm_substream *substream;
+};
+
+struct clk_coeff {
+ u32 mclk;
+ u32 rate;
+ u8 pllnl;
+ u8 pllnh;
+ u8 pllml;
+ u8 pllmh;
+ u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+ "A-law"};
+
+static const struct soc_enum ml26124_adc_companding_enum
+ = SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+
+static const struct soc_enum ml26124_dac_companding_enum
+ = SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+ SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0,
+ 0xf, 1, alclvl),
+ SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0,
+ 7, 0, mingain),
+ SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+ 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+ SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0),
+ SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0),
+ SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1,
+ 1, 0),
+ SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0),
+ SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0),
+ SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0),
+ SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0),
+ SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0),
+ SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+ SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+ SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+ SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+ SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1,
+ 0),
+ SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0),
+};
+
+/* Input mux */
+static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+ "Digital MIC in", "Analog MIC Differential in"};
+
+static const struct soc_enum ml26124_insel_enum =
+ SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, ml26124_input_select);
+
+static const struct snd_kcontrol_new ml26124_input_mux_controls =
+ SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
+
+static const struct snd_kcontrol_new ml26124_line_control =
+ SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ &ml26124_output_mixer_controls[0],
+ ARRAY_SIZE(ml26124_output_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
+ SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ml26124_input_mux_controls),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ml26124_line_control),
+ SND_SOC_DAPM_INPUT("MDIN"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_OUTPUT("SPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+ /* Supply */
+ {"DAC", NULL, "MCLKEN"},
+ {"ADC", NULL, "MCLKEN"},
+ {"DAC", NULL, "PLLEN"},
+ {"ADC", NULL, "PLLEN"},
+ {"DAC", NULL, "PLLOE"},
+ {"ADC", NULL, "PLLOE"},
+
+ /* output mixer */
+ {"Output Mixer", "DAC Switch", "DAC"},
+ {"Output Mixer", "Line in loopback Switch", "LIN"},
+
+ /* outputs */
+ {"LOUT", NULL, "Output Mixer"},
+ {"SPOUT", NULL, "Output Mixer"},
+ {"Line Out Enable", NULL, "LOUT"},
+
+ /* input */
+ {"ADC", NULL, "Input Mux"},
+ {"Input Mux", "Analog MIC SingleEnded in", "PGA"},
+ {"Input Mux", "Analog MIC Differential in", "PGA"},
+ {"PGA", NULL, "MIN"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+ {12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+ /* CLOCK control Register */
+ {0x00, 0x00 }, /* Sampling Rate */
+ {0x02, 0x00}, /* PLL NL */
+ {0x04, 0x00}, /* PLLNH */
+ {0x06, 0x00}, /* PLLML */
+ {0x08, 0x00}, /* MLLMH */
+ {0x0a, 0x00}, /* PLLDIV */
+ {0x0c, 0x00}, /* Clock Enable */
+ {0x0e, 0x00}, /* CLK Input/Output Control */
+
+ /* System Control Register */
+ {0x10, 0x00}, /* Software RESET */
+ {0x12, 0x00}, /* Record/Playback Run */
+ {0x14, 0x00}, /* Mic Input/Output control */
+
+ /* Power Management Register */
+ {0x20, 0x00}, /* Reference Power Management */
+ {0x22, 0x00}, /* Input Power Management */
+ {0x24, 0x00}, /* DAC Power Management */
+ {0x26, 0x00}, /* SP-AMP Power Management */
+ {0x28, 0x00}, /* LINEOUT Power Management */
+ {0x2a, 0x00}, /* VIDEO Power Management */
+ {0x2e, 0x00}, /* AC-CMP Power Management */
+
+ /* Analog reference Control Register */
+ {0x30, 0x04}, /* MICBIAS Voltage Control */
+
+ /* Input/Output Amplifier Control Register */
+ {0x32, 0x10}, /* MIC Input Volume */
+ {0x38, 0x00}, /* Mic Boost Volume */
+ {0x3a, 0x33}, /* Speaker AMP Volume */
+ {0x48, 0x00}, /* AMP Volume Control Function Enable */
+ {0x4a, 0x00}, /* Amplifier Volume Fader Control */
+
+ /* Analog Path Control Register */
+ {0x54, 0x00}, /* Speaker AMP Output Control */
+ {0x5a, 0x00}, /* Mic IF Control */
+ {0xe8, 0x01}, /* Mic Select Control */
+
+ /* Audio Interface Control Register */
+ {0x60, 0x00}, /* SAI-Trans Control */
+ {0x62, 0x00}, /* SAI-Receive Control */
+ {0x64, 0x00}, /* SAI Mode select */
+
+ /* DSP Control Register */
+ {0x66, 0x01}, /* Filter Func Enable */
+ {0x68, 0x00}, /* Volume Control Func Enable */
+ {0x6A, 0x00}, /* Mixer & Volume Control*/
+ {0x6C, 0xff}, /* Record Digital Volume */
+ {0x70, 0xff}, /* Playback Digital Volume */
+ {0x72, 0x10}, /* Digital Boost Volume */
+ {0x74, 0xe7}, /* EQ gain Band0 */
+ {0x76, 0xe7}, /* EQ gain Band1 */
+ {0x78, 0xe7}, /* EQ gain Band2 */
+ {0x7A, 0xe7}, /* EQ gain Band3 */
+ {0x7C, 0xe7}, /* EQ gain Band4 */
+ {0x7E, 0x00}, /* HPF2 CutOff*/
+ {0x80, 0x00}, /* EQ Band0 Coef0L */
+ {0x82, 0x00}, /* EQ Band0 Coef0H */
+ {0x84, 0x00}, /* EQ Band0 Coef0L */
+ {0x86, 0x00}, /* EQ Band0 Coef0H */
+ {0x88, 0x00}, /* EQ Band1 Coef0L */
+ {0x8A, 0x00}, /* EQ Band1 Coef0H */
+ {0x8C, 0x00}, /* EQ Band1 Coef0L */
+ {0x8E, 0x00}, /* EQ Band1 Coef0H */
+ {0x90, 0x00}, /* EQ Band2 Coef0L */
+ {0x92, 0x00}, /* EQ Band2 Coef0H */
+ {0x94, 0x00}, /* EQ Band2 Coef0L */
+ {0x96, 0x00}, /* EQ Band2 Coef0H */
+ {0x98, 0x00}, /* EQ Band3 Coef0L */
+ {0x9A, 0x00}, /* EQ Band3 Coef0H */
+ {0x9C, 0x00}, /* EQ Band3 Coef0L */
+ {0x9E, 0x00}, /* EQ Band3 Coef0H */
+ {0xA0, 0x00}, /* EQ Band4 Coef0L */
+ {0xA2, 0x00}, /* EQ Band4 Coef0H */
+ {0xA4, 0x00}, /* EQ Band4 Coef0L */
+ {0xA6, 0x00}, /* EQ Band4 Coef0H */
+
+ /* ALC Control Register */
+ {0xb0, 0x00}, /* ALC Mode */
+ {0xb2, 0x02}, /* ALC Attack Time */
+ {0xb4, 0x03}, /* ALC Decay Time */
+ {0xb6, 0x00}, /* ALC Hold Time */
+ {0xb8, 0x0b}, /* ALC Target Level */
+ {0xba, 0x70}, /* ALC Max/Min Gain */
+ {0xbc, 0x00}, /* Noise Gate Threshold */
+ {0xbe, 0x00}, /* ALC ZeroCross TimeOut */
+
+ /* Playback Limiter Control Register */
+ {0xc0, 0x04}, /* PL Attack Time */
+ {0xc2, 0x05}, /* PL Decay Time */
+ {0xc4, 0x0d}, /* PL Target Level */
+ {0xc6, 0x70}, /* PL Max/Min Gain */
+ {0xc8, 0x10}, /* Playback Boost Volume */
+ {0xca, 0x00}, /* PL ZeroCross TimeOut */
+
+ /* Video Amplifier Control Register */
+ {0xd0, 0x01}, /* VIDEO AMP Gain Control */
+ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */
+ {0xd4, 0x01}, /* VIDEO AMP Control2 */
+};
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+ int srate;
+
+ switch (rate) {
+ case 16000:
+ srate = 3;
+ break;
+ case 32000:
+ srate = 6;
+ break;
+ case 48000:
+ srate = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int ml26124_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int i = get_coeff(priv->mclk, params_rate(hw_params));
+
+ priv->substream = substream;
+ priv->rate = params_rate(hw_params);
+
+ if (priv->clk_in) {
+ switch (priv->mclk / params_rate(hw_params)) {
+ case 256:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 1);
+ break;
+ case 512:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 2);
+ break;
+ case 1024:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 3);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported MCLKI\n");
+ break;
+ }
+ } else {
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 0);
+ }
+
+ switch (params_rate(hw_params)) {
+ case 16000:
+ snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+ get_srate(params_rate(hw_params)));
+ snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+ coeff_div[i].pllnl);
+ snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+ coeff_div[i].pllnh);
+ snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+ coeff_div[i].pllml);
+ snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+ coeff_div[i].pllmh);
+ snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+ coeff_div[i].plldiv);
+ break;
+ case 32000:
+ snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+ get_srate(params_rate(hw_params)));
+ snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+ coeff_div[i].pllnl);
+ snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+ coeff_div[i].pllnh);
+ snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+ coeff_div[i].pllml);
+ snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+ coeff_div[i].pllmh);
+ snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+ coeff_div[i].plldiv);
+ break;
+ case 48000:
+ snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+ get_srate(params_rate(hw_params)));
+ snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+ coeff_div[i].pllnl);
+ snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+ coeff_div[i].pllnh);
+ snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+ coeff_div[i].pllml);
+ snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+ coeff_div[i].pllmh);
+ snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+ coeff_div[i].plldiv);
+ break;
+ default:
+ pr_err("%s:this rate is no support for ml26124\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (priv->substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+ break;
+ }
+
+ if (mute)
+ snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_ON);
+ else
+ snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_OFF);
+
+ return 0;
+}
+
+static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ unsigned char mode;
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case ML26124_USE_PLLOUT:
+ priv->clk_in = ML26124_USE_PLLOUT;
+ break;
+ case ML26124_USE_MCLKI:
+ priv->clk_in = ML26124_USE_MCLKI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->mclk = freq;
+
+ return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
+ msleep(100);
+ snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK,
+ ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* VMID ON */
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ ML26124_VMID, ML26124_VMID);
+ msleep(500);
+ regcache_sync(priv->regmap);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* VMID OFF */
+ snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ ML26124_VMID, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+ .hw_params = ml26124_hw_params,
+ .digital_mute = ml26124_mute,
+ .set_fmt = ml26124_set_dai_fmt,
+ .set_sysclk = ml26124_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+ .name = "ml26124-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .ops = &ml26124_dai_ops,
+ .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int ml26124_suspend(struct snd_soc_codec *codec)
+{
+ ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int ml26124_resume(struct snd_soc_codec *codec)
+{
+ ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define ml26124_suspend NULL
+#define ml26124_resume NULL
+#endif
+
+static int ml26124_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ codec->control_data = priv->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* Software Reset */
+ snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
+ snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+
+ ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+ .probe = ml26124_probe,
+ .suspend = ml26124_suspend,
+ .resume = ml26124_resume,
+ .set_bias_level = ml26124_set_bias_level,
+ .dapm_widgets = ml26124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+ .dapm_routes = ml26124_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+ .controls = ml26124_snd_controls,
+ .num_controls = ARRAY_SIZE(ml26124_snd_controls),
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = ML26124_NUM_REGISTER,
+ .reg_defaults = ml26124_reg,
+ .num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .write_flag_mask = 0x01,
+};
+
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ml26124_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
+ priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_ml26124, &ml26124_dai, 1);
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+ struct ml26124_priv *priv = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ regmap_exit(priv->regmap);
+ return 0;
+}
+
+static const struct i2c_device_id ml26124_i2c_id[] = {
+ { "ml26124", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
+
+static struct i2c_driver ml26124_i2c_driver = {
+ .driver = {
+ .name = "ml26124",
+ .owner = THIS_MODULE,
+ },
+ .probe = ml26124_i2c_probe,
+ .remove = __devexit_p(ml26124_i2c_remove),
+ .id_table = ml26124_i2c_id,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
new file mode 100644
index 00000000000..5ea0cbb8c46
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE 0x00
+#define ML26124_PLLNL 0x02
+#define ML26124_PLLNH 0x04
+#define ML26124_PLLML 0x06
+#define ML26124_PLLMH 0x08
+#define ML26124_PLLDIV 0x0a
+#define ML26124_CLK_EN 0x0c
+#define ML26124_CLK_CTL 0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST 0x10
+#define ML26124_REC_PLYBAK_RUN 0x12
+#define ML26124_MIC_TIM 0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG 0x20
+#define ML26124_PW_IN_PW_MNG 0x22
+#define ML26124_PW_DAC_PW_MNG 0x24
+#define ML26124_PW_SPAMP_PW_MNG 0x26
+#define ML26124_PW_LOUT_PW_MNG 0x28
+#define ML26124_PW_VOUT_PW_MNG 0x2a
+#define ML26124_PW_ZCCMP_PW_MNG 0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL 0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL 0x32
+#define ML26124_PW_MIC_BOST_VOL 0x38
+#define ML26124_PW_SPK_AMP_VOL 0x3a
+#define ML26124_PW_AMP_VOL_FUNC 0x48
+#define ML26124_PW_AMP_VOL_FADE 0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT 0x54
+#define ML26124_MIC_IF_CTL 0x5a
+#define ML26124_MIC_SELECT 0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL 0x60
+#define ML26124_SAI_RCV_CTL 0x62
+#define ML26124_SAI_MODE_SEL 0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN 0x66
+#define ML26124_DVOL_CTL 0x68
+#define ML26124_MIXER_VOL_CTL 0x6a
+#define ML26124_RECORD_DIG_VOL 0x6c
+#define ML26124_PLBAK_DIG_VOL 0x70
+#define ML26124_DIGI_BOOST_VOL 0x72
+#define ML26124_EQ_GAIN_BRAND0 0x74
+#define ML26124_EQ_GAIN_BRAND1 0x76
+#define ML26124_EQ_GAIN_BRAND2 0x78
+#define ML26124_EQ_GAIN_BRAND3 0x7a
+#define ML26124_EQ_GAIN_BRAND4 0x7c
+#define ML26124_HPF2_CUTOFF 0x7e
+#define ML26124_EQBRAND0_F0L 0x80
+#define ML26124_EQBRAND0_F0H 0x82
+#define ML26124_EQBRAND0_F1L 0x84
+#define ML26124_EQBRAND0_F1H 0x86
+#define ML26124_EQBRAND1_F0L 0x88
+#define ML26124_EQBRAND1_F0H 0x8a
+#define ML26124_EQBRAND1_F1L 0x8c
+#define ML26124_EQBRAND1_F1H 0x8e
+#define ML26124_EQBRAND2_F0L 0x90
+#define ML26124_EQBRAND2_F0H 0x92
+#define ML26124_EQBRAND2_F1L 0x94
+#define ML26124_EQBRAND2_F1H 0x96
+#define ML26124_EQBRAND3_F0L 0x98
+#define ML26124_EQBRAND3_F0H 0x9a
+#define ML26124_EQBRAND3_F1L 0x9c
+#define ML26124_EQBRAND3_F1H 0x9e
+#define ML26124_EQBRAND4_F0L 0xa0
+#define ML26124_EQBRAND4_F0H 0xa2
+#define ML26124_EQBRAND4_F1L 0xa4
+#define ML26124_EQBRAND4_F1H 0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE 0xb0
+#define ML26124_ALC_ATTACK_TIM 0xb2
+#define ML26124_ALC_DECAY_TIM 0xb4
+#define ML26124_ALC_HOLD_TIM 0xb6
+#define ML26124_ALC_TARGET_LEV 0xb8
+#define ML26124_ALC_MAXMIN_GAIN 0xba
+#define ML26124_NOIS_GATE_THRSH 0xbc
+#define ML26124_ALC_ZERO_TIMOUT 0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME 0xc0
+#define ML26124_PL_DECAYTIME 0xc2
+#define ML26124_PL_TARGETTIME 0xc4
+#define ML26124_PL_MAXMIN_GAIN 0xc6
+#define ML26124_PLYBAK_BOST_VOL 0xc8
+#define ML26124_PL_0CROSS_TIMOUT 0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0
+#define ML26124_VIDEO_AMP_SETUP1 0xd2
+#define ML26124_VIDEO_AMP_CTL2 0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL 0
+#define ML26124_USE_MCLKI_256FS 1
+#define ML26124_USE_MCLKI_512FS 2
+#define ML26124_USE_MCLKI_1024FS 3
+
+/* Register Mask */
+#define ML26124_R0_MASK 0xf
+#define ML26124_R2_MASK 0xff
+#define ML26124_R4_MASK 0x1
+#define ML26124_R6_MASK 0xf
+#define ML26124_R8_MASK 0x3f
+#define ML26124_Ra_MASK 0x1f
+#define ML26124_Rc_MASK 0x1f
+#define ML26124_Re_MASK 0x7
+#define ML26124_R10_MASK 0x1
+#define ML26124_R12_MASK 0x17
+#define ML26124_R14_MASK 0x3f
+#define ML26124_R20_MASK 0x47
+#define ML26124_R22_MASK 0xa
+#define ML26124_R24_MASK 0x2
+#define ML26124_R26_MASK 0x1f
+#define ML26124_R28_MASK 0x2
+#define ML26124_R2a_MASK 0x2
+#define ML26124_R2e_MASK 0x2
+#define ML26124_R30_MASK 0x7
+#define ML26124_R32_MASK 0x3f
+#define ML26124_R38_MASK 0x38
+#define ML26124_R3a_MASK 0x3f
+#define ML26124_R48_MASK 0x3
+#define ML26124_R4a_MASK 0x7
+#define ML26124_R54_MASK 0x2a
+#define ML26124_R5a_MASK 0x3
+#define ML26124_Re8_MASK 0x3
+#define ML26124_R60_MASK 0xff
+#define ML26124_R62_MASK 0xff
+#define ML26124_R64_MASK 0x1
+#define ML26124_R66_MASK 0xff
+#define ML26124_R68_MASK 0x3b
+#define ML26124_R6a_MASK 0xf3
+#define ML26124_R6c_MASK 0xff
+#define ML26124_R70_MASK 0xff
+
+#define ML26124_MCLKEN BIT(0)
+#define ML26124_PLLEN BIT(1)
+#define ML26124_PLLOE BIT(2)
+#define ML26124_MCLKOE BIT(3)
+
+#define ML26124_BLT_ALL_ON 0x1f
+#define ML26124_BLT_PREAMP_ON 0x13
+
+#define ML26124_MICBEN_ON BIT(2)
+
+enum ml26124_regs {
+ ML26124_MCLK = 0,
+};
+
+enum ml26124_clk_in {
+ ML26124_USE_PLLOUT = 0,
+ ML26124_USE_MCLKI,
+};
+
+#endif
diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/omap-hdmi.c
new file mode 100644
index 00000000000..1bf5c74f5f9
--- /dev/null
+++ b/sound/soc/codecs/omap-hdmi.c
@@ -0,0 +1,69 @@
+/*
+ * ALSA SoC codec driver for HDMI audio on OMAP processors.
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Ricardo Neri <ricardo.neri@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "hdmi-audio-codec"
+
+static struct snd_soc_codec_driver omap_hdmi_codec;
+
+static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
+ .name = "omap-hdmi-hifi",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+static __devinit int omap_hdmi_codec_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec,
+ &omap_hdmi_codec_dai, 1);
+}
+
+static __devexit int omap_hdmi_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver omap_hdmi_codec_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = omap_hdmi_codec_probe,
+ .remove = __devexit_p(omap_hdmi_codec_remove),
+};
+
+module_platform_driver(omap_hdmi_codec_driver);
+
+MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
+MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 20c324c7c34..960d0e93cce 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -18,7 +18,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -30,6 +30,7 @@
#include "rt5631.h"
struct rt5631_priv {
+ struct regmap *regmap;
int codec_version;
int master;
int sysclk;
@@ -38,33 +39,33 @@ struct rt5631_priv {
int dmic_used_flag;
};
-static const u16 rt5631_reg[RT5631_VENDOR_ID2 + 1] = {
- [RT5631_SPK_OUT_VOL] = 0x8888,
- [RT5631_HP_OUT_VOL] = 0x8080,
- [RT5631_MONO_AXO_1_2_VOL] = 0xa080,
- [RT5631_AUX_IN_VOL] = 0x0808,
- [RT5631_ADC_REC_MIXER] = 0xf0f0,
- [RT5631_VDAC_DIG_VOL] = 0x0010,
- [RT5631_OUTMIXER_L_CTRL] = 0xffc0,
- [RT5631_OUTMIXER_R_CTRL] = 0xffc0,
- [RT5631_AXO1MIXER_CTRL] = 0x88c0,
- [RT5631_AXO2MIXER_CTRL] = 0x88c0,
- [RT5631_DIG_MIC_CTRL] = 0x3000,
- [RT5631_MONO_INPUT_VOL] = 0x8808,
- [RT5631_SPK_MIXER_CTRL] = 0xf8f8,
- [RT5631_SPK_MONO_OUT_CTRL] = 0xfc00,
- [RT5631_SPK_MONO_HP_OUT_CTRL] = 0x4440,
- [RT5631_SDP_CTRL] = 0x8000,
- [RT5631_MONO_SDP_CTRL] = 0x8000,
- [RT5631_STEREO_AD_DA_CLK_CTRL] = 0x2010,
- [RT5631_GEN_PUR_CTRL_REG] = 0x0e00,
- [RT5631_INT_ST_IRQ_CTRL_2] = 0x071a,
- [RT5631_MISC_CTRL] = 0x2040,
- [RT5631_DEPOP_FUN_CTRL_2] = 0x8000,
- [RT5631_SOFT_VOL_CTRL] = 0x07e0,
- [RT5631_ALC_CTRL_1] = 0x0206,
- [RT5631_ALC_CTRL_3] = 0x2000,
- [RT5631_PSEUDO_SPATL_CTRL] = 0x0553,
+static const struct reg_default rt5631_reg[] = {
+ { RT5631_SPK_OUT_VOL, 0x8888 },
+ { RT5631_HP_OUT_VOL, 0x8080 },
+ { RT5631_MONO_AXO_1_2_VOL, 0xa080 },
+ { RT5631_AUX_IN_VOL, 0x0808 },
+ { RT5631_ADC_REC_MIXER, 0xf0f0 },
+ { RT5631_VDAC_DIG_VOL, 0x0010 },
+ { RT5631_OUTMIXER_L_CTRL, 0xffc0 },
+ { RT5631_OUTMIXER_R_CTRL, 0xffc0 },
+ { RT5631_AXO1MIXER_CTRL, 0x88c0 },
+ { RT5631_AXO2MIXER_CTRL, 0x88c0 },
+ { RT5631_DIG_MIC_CTRL, 0x3000 },
+ { RT5631_MONO_INPUT_VOL, 0x8808 },
+ { RT5631_SPK_MIXER_CTRL, 0xf8f8 },
+ { RT5631_SPK_MONO_OUT_CTRL, 0xfc00 },
+ { RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 },
+ { RT5631_SDP_CTRL, 0x8000 },
+ { RT5631_MONO_SDP_CTRL, 0x8000 },
+ { RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 },
+ { RT5631_GEN_PUR_CTRL_REG, 0x0e00 },
+ { RT5631_INT_ST_IRQ_CTRL_2, 0x071a },
+ { RT5631_MISC_CTRL, 0x2040 },
+ { RT5631_DEPOP_FUN_CTRL_2, 0x8000 },
+ { RT5631_SOFT_VOL_CTRL, 0x07e0 },
+ { RT5631_ALC_CTRL_1, 0x0206 },
+ { RT5631_ALC_CTRL_3, 0x2000 },
+ { RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
};
/**
@@ -96,8 +97,7 @@ static int rt5631_reset(struct snd_soc_codec *codec)
return snd_soc_write(codec, RT5631_RESET, 0);
}
-static int rt5631_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5631_RESET:
@@ -111,8 +111,7 @@ static int rt5631_volatile_register(struct snd_soc_codec *codec,
}
}
-static int rt5631_readable_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool rt5631_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5631_RESET:
@@ -1361,8 +1360,7 @@ static int get_coeff(int mclk, int rate, int timesofbclk)
static int rt5631_hifi_pcm_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 snd_soc_codec *codec = dai->codec;
struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
int timesofbclk = 32, coeff;
unsigned int iface = 0;
@@ -1544,6 +1542,8 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
static int rt5631_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
@@ -1561,8 +1561,8 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_FAST_VREF_CTRL,
RT5631_PWR_FAST_VREF_CTRL);
- codec->cache_only = false;
- snd_soc_cache_sync(codec);
+ regcache_cache_only(rt5631->regmap, false);
+ regcache_sync(rt5631->regmap);
}
break;
@@ -1587,7 +1587,9 @@ static int rt5631_probe(struct snd_soc_codec *codec)
unsigned int val;
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ codec->control_data = rt5631->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;
@@ -1698,12 +1700,6 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
.suspend = rt5631_suspend,
.resume = rt5631_resume,
.set_bias_level = rt5631_set_bias_level,
- .reg_cache_size = RT5631_VENDOR_ID2 + 1,
- .reg_word_size = sizeof(u16),
- .reg_cache_default = rt5631_reg,
- .volatile_register = rt5631_volatile_register,
- .readable_register = rt5631_readable_register,
- .reg_cache_step = 1,
.controls = rt5631_snd_controls,
.num_controls = ARRAY_SIZE(rt5631_snd_controls),
.dapm_widgets = rt5631_dapm_widgets,
@@ -1718,6 +1714,18 @@ static const struct i2c_device_id rt5631_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
+static const struct regmap_config rt5631_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .readable_reg = rt5631_readable_register,
+ .volatile_reg = rt5631_volatile_register,
+ .max_register = RT5631_VENDOR_ID2,
+ .reg_defaults = rt5631_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5631_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
static int rt5631_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1731,6 +1739,10 @@ static int rt5631_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5631);
+ rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config);
+ if (IS_ERR(rt5631->regmap))
+ return PTR_ERR(rt5631->regmap);
+
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631,
rt5631_dai, ARRAY_SIZE(rt5631_dai));
return ret;
@@ -1752,17 +1764,7 @@ static struct i2c_driver rt5631_i2c_driver = {
.id_table = rt5631_i2c_id,
};
-static int __init rt5631_modinit(void)
-{
- return i2c_add_driver(&rt5631_i2c_driver);
-}
-module_init(rt5631_modinit);
-
-static void __exit rt5631_modexit(void)
-{
- i2c_del_driver(&rt5631_i2c_driver);
-}
-module_exit(rt5631_modexit);
+module_i2c_driver(rt5631_i2c_driver);
MODULE_DESCRIPTION("ASoC RT5631 driver");
MODULE_AUTHOR("flove <flove@realtek.com>");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index c395ec37044..8af6a5245b1 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -84,8 +84,8 @@ static struct regulator_consumer_supply ldo_consumer[] = {
static struct regulator_init_data ldo_init_data = {
.constraints = {
- .min_uV = 850000,
- .max_uV = 1600000,
+ .min_uV = 1200000,
+ .max_uV = 1200000,
.valid_modes_mask = REGULATOR_MODE_NORMAL,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
@@ -197,9 +197,9 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("HP_OUT"),
SND_SOC_DAPM_OUTPUT("LINE_OUT"),
- SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
- mic_bias_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0),
@@ -665,8 +665,7 @@ static int sgtl5000_pcm_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 snd_soc_codec *codec = dai->codec;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
int channels = params_channels(params);
int i2s_ctl = 0;
@@ -1455,17 +1454,7 @@ static struct i2c_driver sgtl5000_i2c_driver = {
.id_table = sgtl5000_id,
};
-static int __init sgtl5000_modinit(void)
-{
- return i2c_add_driver(&sgtl5000_i2c_driver);
-}
-module_init(sgtl5000_modinit);
-
-static void __exit sgtl5000_exit(void)
-{
- i2c_del_driver(&sgtl5000_i2c_driver);
-}
-module_exit(sgtl5000_exit);
+module_i2c_driver(sgtl5000_i2c_driver);
MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver");
MODULE_AUTHOR("Zeng Zhaoming <zengzm.kernel@gmail.com>");
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index de2b20544ce..079066fef42 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,8 +44,6 @@
#include "ssm2602.h"
-#define SSM2602_VERSION "0.1"
-
enum ssm2602_type {
SSM2602,
SSM2604,
@@ -53,10 +52,12 @@ enum ssm2602_type {
/* codec private data */
struct ssm2602_priv {
unsigned int sysclk;
- enum snd_soc_control_type control_type;
+ struct snd_pcm_hw_constraint_list *sysclk_constraints;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
+ struct regmap *regmap;
+
enum ssm2602_type type;
unsigned int clk_out_pwr;
};
@@ -73,7 +74,6 @@ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
0x0000, 0x0000
};
-#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
/*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = {
@@ -195,6 +195,24 @@ static const struct snd_soc_dapm_route ssm2604_routes[] = {
{"ADC", NULL, "Line Input"},
};
+static const unsigned int ssm2602_rates_12288000[] = {
+ 8000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
+ .list = ssm2602_rates_12288000,
+ .count = ARRAY_SIZE(ssm2602_rates_12288000),
+};
+
+static const unsigned int ssm2602_rates_11289600[] = {
+ 8000, 44100, 88200,
+};
+
+static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
+ .list = ssm2602_rates_11289600,
+ .count = ARRAY_SIZE(ssm2602_rates_11289600),
+};
+
struct ssm2602_coeff {
u32 mclk;
u32 rate;
@@ -254,11 +272,10 @@ static int ssm2602_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 snd_soc_codec *codec = dai->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
+ unsigned int iface;
if (substream == ssm2602->slave_substream) {
dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
@@ -268,31 +285,34 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
if (srate < 0)
return srate;
- snd_soc_write(codec, SSM2602_SRATE, srate);
+ regmap_write(ssm2602->regmap, SSM2602_SRATE, srate);
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
+ iface = 0x0;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
- iface |= 0x0004;
+ iface = 0x4;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- iface |= 0x0008;
+ iface = 0x8;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- iface |= 0x000c;
+ iface = 0xc;
break;
+ default:
+ return -EINVAL;
}
- snd_soc_write(codec, SSM2602_IFACE, iface);
+ regmap_update_bits(ssm2602->regmap, SSM2602_IFACE,
+ IFACE_AUDIO_DATA_LEN, iface);
return 0;
}
static int ssm2602_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
struct snd_pcm_runtime *master_runtime;
@@ -322,14 +342,19 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
} else
ssm2602->master_substream = substream;
+ if (ssm2602->sysclk_constraints) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ ssm2602->sysclk_constraints);
+ }
+
return 0;
}
static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
if (ssm2602->master_substream == substream)
@@ -341,14 +366,14 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
if (mute)
- snd_soc_update_bits(codec, SSM2602_APDIGI,
+ regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
APDIGI_ENABLE_DAC_MUTE,
APDIGI_ENABLE_DAC_MUTE);
else
- snd_soc_update_bits(codec, SSM2602_APDIGI,
+ regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
APDIGI_ENABLE_DAC_MUTE, 0);
return 0;
}
@@ -364,16 +389,21 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return -EINVAL;
switch (freq) {
- case 11289600:
- case 12000000:
case 12288000:
- case 16934400:
case 18432000:
- ssm2602->sysclk = freq;
+ ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
+ break;
+ case 11289600:
+ case 16934400:
+ ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
+ break;
+ case 12000000:
+ ssm2602->sysclk_constraints = NULL;
break;
default:
return -EINVAL;
}
+ ssm2602->sysclk = freq;
} else {
unsigned int mask;
@@ -393,7 +423,7 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
else
ssm2602->clk_out_pwr &= ~mask;
- snd_soc_update_bits(codec, SSM2602_PWR,
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr);
}
@@ -403,8 +433,8 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = 0;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+ unsigned int iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -455,7 +485,7 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- snd_soc_write(codec, SSM2602_IFACE, iface);
+ regmap_write(ssm2602->regmap, SSM2602_IFACE, iface);
return 0;
}
@@ -467,7 +497,7 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid on, osc and clkout on if enabled */
- snd_soc_update_bits(codec, SSM2602_PWR,
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
ssm2602->clk_out_pwr);
break;
@@ -475,13 +505,13 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_update_bits(codec, SSM2602_PWR,
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
PWR_CLK_OUT_PDN | PWR_OSC_PDN);
break;
case SND_SOC_BIAS_OFF:
/* everything off */
- snd_soc_update_bits(codec, SSM2602_PWR,
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
PWR_POWER_OFF, PWR_POWER_OFF);
break;
@@ -540,12 +570,13 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
static int ssm2602_probe(struct snd_soc_codec *codec)
{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
- snd_soc_update_bits(codec, SSM2602_LOUT1V,
+ regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH);
- snd_soc_update_bits(codec, SSM2602_ROUT1V,
+ regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
@@ -581,27 +612,26 @@ static int ssm260x_probe(struct snd_soc_codec *codec)
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
int ret;
- pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
-
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
+ codec->control_data = ssm2602->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;
}
- ret = ssm2602_reset(codec);
+ ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
/* set the update bits */
- snd_soc_update_bits(codec, SSM2602_LINVOL,
+ regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
- snd_soc_update_bits(codec, SSM2602_RINVOL,
+ regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL,
RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH);
/*select Line in as default input*/
- snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
+ regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST);
switch (ssm2602->type) {
@@ -634,9 +664,6 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
.suspend = ssm2602_suspend,
.resume = ssm2602_resume,
.set_bias_level = ssm2602_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = ssm2602_reg,
.controls = ssm260x_snd_controls,
.num_controls = ARRAY_SIZE(ssm260x_snd_controls),
@@ -646,6 +673,23 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
.num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
};
+static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == SSM2602_RESET;
+}
+
+static const struct regmap_config ssm2602_regmap_config = {
+ .val_bits = 9,
+ .reg_bits = 7,
+
+ .max_register = SSM2602_RESET,
+ .volatile_reg = ssm2602_register_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults_raw = ssm2602_reg,
+ .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit ssm2602_spi_probe(struct spi_device *spi)
{
@@ -658,9 +702,12 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, ssm2602);
- ssm2602->control_type = SND_SOC_SPI;
ssm2602->type = SSM2602;
+ ssm2602->regmap = devm_regmap_init_spi(spi, &ssm2602_regmap_config);
+ if (IS_ERR(ssm2602->regmap))
+ return PTR_ERR(ssm2602->regmap);
+
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
return ret;
@@ -701,9 +748,12 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, ssm2602);
- ssm2602->control_type = SND_SOC_I2C;
ssm2602->type = id->driver_data;
+ ssm2602->regmap = devm_regmap_init_i2c(i2c, &ssm2602_regmap_config);
+ if (IS_ERR(ssm2602->regmap))
+ return PTR_ERR(ssm2602->regmap);
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
return ret;
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 7db6fa51502..8d717f4b5a8 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -609,8 +609,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
unsigned int rate;
int i, mcs = -1, ir = -1;
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index df1e07ffac3..31762ebdd77 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -34,8 +34,6 @@
#include "tlv320aic23.h"
-#define AIC23_VERSION "0.1"
-
/*
* AIC23 register cache
*/
@@ -325,8 +323,7 @@ static int tlv320aic23_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 snd_soc_codec *codec = dai->codec;
u16 iface_reg;
int ret;
struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
@@ -371,8 +368,7 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
/* set active */
snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001);
@@ -383,8 +379,7 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
/* deactivate */
@@ -548,8 +543,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec)
struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
int ret;
- printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
-
ret = snd_soc_codec_set_cache_io(codec, 7, 9, aic23->control_type);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 802064b5030..85944e95357 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -126,8 +126,7 @@ static int aic26_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 snd_soc_codec *codec = dai->codec;
struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
int fsref, divisor, wlen, pval, jval, dval, qval;
u16 reg;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 8d20f6ec20f..64d2a4fa34b 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -802,8 +802,7 @@ static int aic3x_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 snd_soc_codec *codec = dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
@@ -1161,24 +1160,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
- int headset_debounce, int button_debounce)
-{
- u8 val;
-
- val = ((detect & AIC3X_HEADSET_DETECT_MASK)
- << AIC3X_HEADSET_DETECT_SHIFT) |
- ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
- << AIC3X_HEADSET_DEBOUNCE_SHIFT) |
- ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
- << AIC3X_BUTTON_DEBOUNCE_SHIFT);
-
- if (detect & AIC3X_HEADSET_DETECT_MASK)
- val |= AIC3X_HEADSET_DETECT_ENABLED;
-
- snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
-}
-
#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 4587ddd0fbf..0dd41077ab7 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -62,8 +62,10 @@
#define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \
(((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate))))
-static void dac33_calculate_times(struct snd_pcm_substream *substream);
-static int dac33_prepare_chip(struct snd_pcm_substream *substream);
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec);
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec);
enum dac33_state {
DAC33_IDLE = 0,
@@ -427,8 +429,8 @@ static int dac33_playback_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (likely(dac33->substream)) {
- dac33_calculate_times(dac33->substream);
- dac33_prepare_chip(dac33->substream);
+ dac33_calculate_times(dac33->substream, w->codec);
+ dac33_prepare_chip(dac33->substream, w->codec);
}
break;
case SND_SOC_DAPM_POST_PMD:
@@ -799,8 +801,7 @@ static void dac33_oscwait(struct snd_soc_codec *codec)
static int dac33_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
/* Stream started, save the substream pointer */
@@ -812,8 +813,7 @@ static int dac33_startup(struct snd_pcm_substream *substream,
static void dac33_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
dac33->substream = NULL;
@@ -825,8 +825,7 @@ static int dac33_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 snd_soc_codec *codec = dai->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
/* Check parameters for validity */
@@ -868,10 +867,9 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
* writes happens in different order, than dac33 might end up in unknown state.
* Use the known, working sequence of register writes to initialize the dac33.
*/
-static int dac33_prepare_chip(struct snd_pcm_substream *substream)
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
u8 aictrl_a, aictrl_b, fifoctrl_a;
@@ -1067,10 +1065,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
return 0;
}
-static void dac33_calculate_times(struct snd_pcm_substream *substream)
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned int period_size = substream->runtime->period_size;
unsigned int rate = substream->runtime->rate;
@@ -1128,8 +1125,7 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream)
static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
@@ -1161,8 +1157,7 @@ static snd_pcm_sframes_t dac33_dai_delay(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned long long t0, t1, t_now;
unsigned int time_delta, uthr;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 170cf9a8fc7..391fcfc7b63 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1685,8 +1685,7 @@ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
static int twl4030_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
if (twl4030->master_substream) {
@@ -1715,8 +1714,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
static void twl4030_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
if (twl4030->master_substream == substream)
@@ -1740,8 +1738,7 @@ static int twl4030_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 snd_soc_codec *codec = dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 mode, old_mode, format, old_format;
@@ -1974,8 +1971,7 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
static int twl4030_voice_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 mode;
@@ -2007,8 +2003,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
/* Enable voice digital filters */
twl4030_voice_enable(codec, substream->stream, 0);
@@ -2017,8 +2012,7 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
static int twl4030_voice_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 snd_soc_codec *codec = dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 old_mode, mode;
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index dc7509b9d53..a36e9fcdf18 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -46,17 +46,6 @@
#define TWL6040_OUTHF_0dB 0x03
#define TWL6040_OUTHF_M52dB 0x1D
-#define TWL6040_RAMP_NONE 0
-#define TWL6040_RAMP_UP 1
-#define TWL6040_RAMP_DOWN 2
-
-#define TWL6040_HSL_VOL_MASK 0x0F
-#define TWL6040_HSL_VOL_SHIFT 0
-#define TWL6040_HSR_VOL_MASK 0xF0
-#define TWL6040_HSR_VOL_SHIFT 4
-#define TWL6040_HF_VOL_MASK 0x1F
-#define TWL6040_HF_VOL_SHIFT 0
-
/* Shadow register used by the driver */
#define TWL6040_REG_SW_SHADOW 0x2F
#define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1)
@@ -64,18 +53,6 @@
/* TWL6040_REG_SW_SHADOW (0x2F) fields */
#define TWL6040_EAR_PATH_ENABLE 0x01
-struct twl6040_output {
- u16 active;
- u16 left_vol;
- u16 right_vol;
- u16 left_step;
- u16 right_step;
- unsigned int step_delay;
- u16 ramp;
- struct delayed_work work;
- struct completion ramp_done;
-};
-
struct twl6040_jack_data {
struct snd_soc_jack *jack;
struct delayed_work work;
@@ -100,8 +77,6 @@ struct twl6040_data {
struct snd_soc_codec *codec;
struct workqueue_struct *workqueue;
struct mutex mutex;
- struct twl6040_output headset;
- struct twl6040_output handsfree;
};
/*
@@ -311,318 +286,6 @@ static void twl6040_restore_regs(struct snd_soc_codec *codec)
}
}
-/*
- * Ramp HS PGA volume to minimise pops at stream startup and shutdown.
- */
-static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec,
- unsigned int left_step, unsigned int right_step)
-{
-
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040_output *headset = &priv->headset;
- int left_complete = 0, right_complete = 0;
- u8 reg, val;
-
- /* left channel */
- left_step = (left_step > 0xF) ? 0xF : left_step;
- reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN);
- val = (~reg & TWL6040_HSL_VOL_MASK);
-
- if (headset->ramp == TWL6040_RAMP_UP) {
- /* ramp step up */
- if (val < headset->left_vol) {
- if (val + left_step > headset->left_vol)
- val = headset->left_vol;
- else
- val += left_step;
-
- reg &= ~TWL6040_HSL_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HSGAIN,
- (reg | (~val & TWL6040_HSL_VOL_MASK)));
- } else {
- left_complete = 1;
- }
- } else if (headset->ramp == TWL6040_RAMP_DOWN) {
- /* ramp step down */
- if (val > 0x0) {
- if ((int)val - (int)left_step < 0)
- val = 0;
- else
- val -= left_step;
-
- reg &= ~TWL6040_HSL_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HSGAIN, reg |
- (~val & TWL6040_HSL_VOL_MASK));
- } else {
- left_complete = 1;
- }
- }
-
- /* right channel */
- right_step = (right_step > 0xF) ? 0xF : right_step;
- reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN);
- val = (~reg & TWL6040_HSR_VOL_MASK) >> TWL6040_HSR_VOL_SHIFT;
-
- if (headset->ramp == TWL6040_RAMP_UP) {
- /* ramp step up */
- if (val < headset->right_vol) {
- if (val + right_step > headset->right_vol)
- val = headset->right_vol;
- else
- val += right_step;
-
- reg &= ~TWL6040_HSR_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HSGAIN,
- (reg | (~val << TWL6040_HSR_VOL_SHIFT)));
- } else {
- right_complete = 1;
- }
- } else if (headset->ramp == TWL6040_RAMP_DOWN) {
- /* ramp step down */
- if (val > 0x0) {
- if ((int)val - (int)right_step < 0)
- val = 0;
- else
- val -= right_step;
-
- reg &= ~TWL6040_HSR_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HSGAIN,
- reg | (~val << TWL6040_HSR_VOL_SHIFT));
- } else {
- right_complete = 1;
- }
- }
-
- return left_complete & right_complete;
-}
-
-/*
- * Ramp HF PGA volume to minimise pops at stream startup and shutdown.
- */
-static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec,
- unsigned int left_step, unsigned int right_step)
-{
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040_output *handsfree = &priv->handsfree;
- int left_complete = 0, right_complete = 0;
- u16 reg, val;
-
- /* left channel */
- left_step = (left_step > 0x1D) ? 0x1D : left_step;
- reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN);
- reg = 0x1D - reg;
- val = (reg & TWL6040_HF_VOL_MASK);
- if (handsfree->ramp == TWL6040_RAMP_UP) {
- /* ramp step up */
- if (val < handsfree->left_vol) {
- if (val + left_step > handsfree->left_vol)
- val = handsfree->left_vol;
- else
- val += left_step;
-
- reg &= ~TWL6040_HF_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HFLGAIN,
- reg | (0x1D - val));
- } else {
- left_complete = 1;
- }
- } else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
- /* ramp step down */
- if (val > 0) {
- if ((int)val - (int)left_step < 0)
- val = 0;
- else
- val -= left_step;
-
- reg &= ~TWL6040_HF_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HFLGAIN,
- reg | (0x1D - val));
- } else {
- left_complete = 1;
- }
- }
-
- /* right channel */
- right_step = (right_step > 0x1D) ? 0x1D : right_step;
- reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN);
- reg = 0x1D - reg;
- val = (reg & TWL6040_HF_VOL_MASK);
- if (handsfree->ramp == TWL6040_RAMP_UP) {
- /* ramp step up */
- if (val < handsfree->right_vol) {
- if (val + right_step > handsfree->right_vol)
- val = handsfree->right_vol;
- else
- val += right_step;
-
- reg &= ~TWL6040_HF_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HFRGAIN,
- reg | (0x1D - val));
- } else {
- right_complete = 1;
- }
- } else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
- /* ramp step down */
- if (val > 0) {
- if ((int)val - (int)right_step < 0)
- val = 0;
- else
- val -= right_step;
-
- reg &= ~TWL6040_HF_VOL_MASK;
- twl6040_write(codec, TWL6040_REG_HFRGAIN,
- reg | (0x1D - val));
- }
- }
-
- return left_complete & right_complete;
-}
-
-/*
- * This work ramps both output PGAs at stream start/stop time to
- * minimise pop associated with DAPM power switching.
- */
-static void twl6040_pga_hs_work(struct work_struct *work)
-{
- struct twl6040_data *priv =
- container_of(work, struct twl6040_data, headset.work.work);
- struct snd_soc_codec *codec = priv->codec;
- struct twl6040_output *headset = &priv->headset;
- int i, headset_complete;
-
- /* do we need to ramp at all ? */
- if (headset->ramp == TWL6040_RAMP_NONE)
- return;
-
- /* HS PGA gain range: 0x0 - 0xf (0 - 15) */
- for (i = 0; i < 16; i++) {
- headset_complete = twl6040_hs_ramp_step(codec,
- headset->left_step,
- headset->right_step);
-
- /* ramp finished ? */
- if (headset_complete)
- break;
-
- schedule_timeout_interruptible(
- msecs_to_jiffies(headset->step_delay));
- }
-
- if (headset->ramp == TWL6040_RAMP_DOWN) {
- headset->active = 0;
- complete(&headset->ramp_done);
- } else {
- headset->active = 1;
- }
- headset->ramp = TWL6040_RAMP_NONE;
-}
-
-static void twl6040_pga_hf_work(struct work_struct *work)
-{
- struct twl6040_data *priv =
- container_of(work, struct twl6040_data, handsfree.work.work);
- struct snd_soc_codec *codec = priv->codec;
- struct twl6040_output *handsfree = &priv->handsfree;
- int i, handsfree_complete;
-
- /* do we need to ramp at all ? */
- if (handsfree->ramp == TWL6040_RAMP_NONE)
- return;
-
- /*
- * HF PGA gain range: 0x00 - 0x1d (0 - 29) */
- for (i = 0; i < 30; i++) {
- handsfree_complete = twl6040_hf_ramp_step(codec,
- handsfree->left_step,
- handsfree->right_step);
-
- /* ramp finished ? */
- if (handsfree_complete)
- break;
-
- schedule_timeout_interruptible(
- msecs_to_jiffies(handsfree->step_delay));
- }
-
-
- if (handsfree->ramp == TWL6040_RAMP_DOWN) {
- handsfree->active = 0;
- complete(&handsfree->ramp_done);
- } else
- handsfree->active = 1;
- handsfree->ramp = TWL6040_RAMP_NONE;
-}
-
-static int out_drv_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040_output *out;
- struct delayed_work *work;
-
- switch (w->shift) {
- case 2: /* Headset output driver */
- out = &priv->headset;
- work = &out->work;
- /*
- * Make sure, that we do not mess up variables for already
- * executing work.
- */
- cancel_delayed_work_sync(work);
-
- out->left_step = priv->hs_left_step;
- out->right_step = priv->hs_right_step;
- out->step_delay = 5; /* 5 ms between volume ramp steps */
- break;
- case 4: /* Handsfree output driver */
- out = &priv->handsfree;
- work = &out->work;
- /*
- * Make sure, that we do not mess up variables for already
- * executing work.
- */
- cancel_delayed_work_sync(work);
-
- out->left_step = priv->hf_left_step;
- out->right_step = priv->hf_right_step;
- out->step_delay = 5; /* 5 ms between volume ramp steps */
- break;
- default:
- return -1;
- }
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- if (out->active)
- break;
-
- /* don't use volume ramp for power-up */
- out->ramp = TWL6040_RAMP_UP;
- out->left_step = out->left_vol;
- out->right_step = out->right_vol;
-
- queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1));
- break;
-
- case SND_SOC_DAPM_PRE_PMD:
- if (!out->active)
- break;
-
- /* use volume ramp for power-down */
- out->ramp = TWL6040_RAMP_DOWN;
- INIT_COMPLETION(out->ramp_done);
-
- queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1));
-
- wait_for_completion_timeout(&out->ramp_done,
- msecs_to_jiffies(2000));
- break;
- }
-
- return 0;
-}
-
/* set headset dac and driver power mode */
static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
{
@@ -747,71 +410,6 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static int twl6040_put_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040_output *out = NULL;
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int ret;
-
- /* For HS and HF we shadow the values and only actually write
- * them out when active in order to ensure the amplifier comes on
- * as quietly as possible. */
- switch (mc->reg) {
- case TWL6040_REG_HSGAIN:
- out = &twl6040_priv->headset;
- break;
- case TWL6040_REG_HFLGAIN:
- out = &twl6040_priv->handsfree;
- break;
- default:
- dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n",
- __func__, mc->reg);
- return -EINVAL;
- }
-
- out->left_vol = ucontrol->value.integer.value[0];
- out->right_vol = ucontrol->value.integer.value[1];
- if (!out->active)
- return 1;
-
- ret = snd_soc_put_volsw(kcontrol, ucontrol);
- if (ret < 0)
- return ret;
-
- return 1;
-}
-
-static int twl6040_get_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040_output *out = &twl6040_priv->headset;
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
-
- switch (mc->reg) {
- case TWL6040_REG_HSGAIN:
- out = &twl6040_priv->headset;
- break;
- case TWL6040_REG_HFLGAIN:
- out = &twl6040_priv->handsfree;
- break;
- default:
- dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n",
- __func__, mc->reg);
- return -EINVAL;
- }
-
- ucontrol->value.integer.value[0] = out->left_vol;
- ucontrol->value.integer.value[1] = out->right_vol;
- return 0;
-}
-
static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1076,12 +674,10 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
/* Playback gains */
- SOC_DOUBLE_EXT_TLV("Headset Playback Volume",
- TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, twl6040_get_volsw,
- twl6040_put_volsw, hs_tlv),
- SOC_DOUBLE_R_EXT_TLV("Handsfree Playback Volume",
- TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1,
- twl6040_get_volsw, twl6040_put_volsw, hf_tlv),
+ SOC_DOUBLE_TLV("Headset Playback Volume",
+ TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
+ SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+ TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
SOC_SINGLE_TLV("Earphone Playback Volume",
TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
@@ -1180,22 +776,14 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
&auxr_switch_control),
/* Analog playback drivers */
- SND_SOC_DAPM_OUT_DRV_E("HF Left Driver",
- TWL6040_REG_HFLCTL, 4, 0, NULL, 0,
- out_drv_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_OUT_DRV_E("HF Right Driver",
- TWL6040_REG_HFRCTL, 4, 0, NULL, 0,
- out_drv_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_OUT_DRV_E("HS Left Driver",
- TWL6040_REG_HSLCTL, 2, 0, NULL, 0,
- out_drv_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_OUT_DRV_E("HS Right Driver",
- TWL6040_REG_HSRCTL, 2, 0, NULL, 0,
- out_drv_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV("HF Left Driver",
+ TWL6040_REG_HFLCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HF Right Driver",
+ TWL6040_REG_HFRCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Left Driver",
+ TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Right Driver",
+ TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV_E("Earphone Driver",
TWL6040_REG_EARCTL, 0, 0, NULL, 0,
twl6040_ep_drv_event,
@@ -1339,8 +927,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
static int twl6040_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -1354,8 +941,7 @@ static int twl6040_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 snd_soc_codec *codec = dai->codec;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int rate;
@@ -1391,8 +977,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
static int twl6040_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct twl6040 *twl6040 = codec->control_data;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -1570,14 +1155,9 @@ static int twl6040_probe(struct snd_soc_codec *codec)
}
INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
- INIT_DELAYED_WORK(&priv->headset.work, twl6040_pga_hs_work);
- INIT_DELAYED_WORK(&priv->handsfree.work, twl6040_pga_hf_work);
mutex_init(&priv->mutex);
- init_completion(&priv->headset.ramp_done);
- init_completion(&priv->handsfree.ramp_done);
-
ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
0, "twl6040_irq_plug", codec);
if (ret) {
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 797b0dde2c6..6c3d43b8ee8 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -159,8 +159,7 @@ static int uda134x_mute(struct snd_soc_dai *dai, int mute)
static int uda134x_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec =rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_pcm_runtime *master_runtime;
@@ -191,8 +190,7 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
static void uda134x_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
if (uda134x->master_substream == substream)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 4f1b23d7e40..2502214b84a 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -502,8 +502,7 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
@@ -528,8 +527,7 @@ static int uda1380_pcm_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 snd_soc_codec *codec = dai->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 3d868dc4009..7b24d6d192e 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -293,8 +293,7 @@ static const struct snd_kcontrol_new wl1273_controls[] = {
static int wl1273_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
switch (wl1273->mode) {
@@ -329,8 +328,7 @@ static int wl1273_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 wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(dai->codec);
struct wl1273_core *core = wl1273->core;
unsigned int rate, width, r;
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index aefb4f89be0..e0b51e9f8b1 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -79,22 +79,65 @@ static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
{ "WM1250 Output", NULL, "DAC" },
};
+static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wm1250_priv *wm1250 = snd_soc_codec_get_drvdata(dai->codec);
+
+ switch (params_rate(params)) {
+ case 8000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 1);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 1);
+ break;
+ case 16000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 0);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 1);
+ break;
+ case 32000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 1);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 0);
+ break;
+ case 64000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 0);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm1250_ev1_ops = {
+ .hw_params = wm1250_ev1_hw_params,
+};
+
static struct snd_soc_dai_driver wm1250_ev1_dai = {
.name = "wm1250-ev1",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
+ .ops = &wm1250_ev1_ops,
};
static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
@@ -215,23 +258,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
.id_table = wm1250_ev1_i2c_id,
};
-static int __init wm1250_ev1_modinit(void)
-{
- int ret = 0;
-
- ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
- if (ret != 0)
- pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
-
- return ret;
-}
-module_init(wm1250_ev1_modinit);
-
-static void __exit wm1250_ev1_exit(void)
-{
- i2c_del_driver(&wm1250_ev1_i2c_driver);
-}
-module_exit(wm1250_ev1_exit);
+module_i2c_driver(wm1250_ev1_i2c_driver);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c
index 9a18fae6820..e167207a19c 100644
--- a/sound/soc/codecs/wm5100-tables.c
+++ b/sound/soc/codecs/wm5100-tables.c
@@ -32,7 +32,18 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg)
case WM5100_MIC_DETECT_3:
return 1;
default:
- return 0;
+ if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+ (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+ (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+ (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+ (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+ (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+ (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+ (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+ (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+ return 1;
+ else
+ return 0;
}
}
@@ -697,9 +708,110 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg)
case WM5100_HPLPF3_2:
case WM5100_HPLPF4_1:
case WM5100_HPLPF4_2:
+ case WM5100_DSP1_CONTROL_1:
+ case WM5100_DSP1_CONTROL_2:
+ case WM5100_DSP1_CONTROL_3:
+ case WM5100_DSP1_CONTROL_4:
+ case WM5100_DSP1_CONTROL_5:
+ case WM5100_DSP1_CONTROL_6:
+ case WM5100_DSP1_CONTROL_7:
+ case WM5100_DSP1_CONTROL_8:
+ case WM5100_DSP1_CONTROL_9:
+ case WM5100_DSP1_CONTROL_10:
+ case WM5100_DSP1_CONTROL_11:
+ case WM5100_DSP1_CONTROL_12:
+ case WM5100_DSP1_CONTROL_13:
+ case WM5100_DSP1_CONTROL_14:
+ case WM5100_DSP1_CONTROL_15:
+ case WM5100_DSP1_CONTROL_16:
+ case WM5100_DSP1_CONTROL_17:
+ case WM5100_DSP1_CONTROL_18:
+ case WM5100_DSP1_CONTROL_19:
+ case WM5100_DSP1_CONTROL_20:
+ case WM5100_DSP1_CONTROL_21:
+ case WM5100_DSP1_CONTROL_22:
+ case WM5100_DSP1_CONTROL_23:
+ case WM5100_DSP1_CONTROL_24:
+ case WM5100_DSP1_CONTROL_25:
+ case WM5100_DSP1_CONTROL_26:
+ case WM5100_DSP1_CONTROL_27:
+ case WM5100_DSP1_CONTROL_28:
+ case WM5100_DSP1_CONTROL_29:
+ case WM5100_DSP1_CONTROL_30:
+ case WM5100_DSP2_CONTROL_1:
+ case WM5100_DSP2_CONTROL_2:
+ case WM5100_DSP2_CONTROL_3:
+ case WM5100_DSP2_CONTROL_4:
+ case WM5100_DSP2_CONTROL_5:
+ case WM5100_DSP2_CONTROL_6:
+ case WM5100_DSP2_CONTROL_7:
+ case WM5100_DSP2_CONTROL_8:
+ case WM5100_DSP2_CONTROL_9:
+ case WM5100_DSP2_CONTROL_10:
+ case WM5100_DSP2_CONTROL_11:
+ case WM5100_DSP2_CONTROL_12:
+ case WM5100_DSP2_CONTROL_13:
+ case WM5100_DSP2_CONTROL_14:
+ case WM5100_DSP2_CONTROL_15:
+ case WM5100_DSP2_CONTROL_16:
+ case WM5100_DSP2_CONTROL_17:
+ case WM5100_DSP2_CONTROL_18:
+ case WM5100_DSP2_CONTROL_19:
+ case WM5100_DSP2_CONTROL_20:
+ case WM5100_DSP2_CONTROL_21:
+ case WM5100_DSP2_CONTROL_22:
+ case WM5100_DSP2_CONTROL_23:
+ case WM5100_DSP2_CONTROL_24:
+ case WM5100_DSP2_CONTROL_25:
+ case WM5100_DSP2_CONTROL_26:
+ case WM5100_DSP2_CONTROL_27:
+ case WM5100_DSP2_CONTROL_28:
+ case WM5100_DSP2_CONTROL_29:
+ case WM5100_DSP2_CONTROL_30:
+ case WM5100_DSP3_CONTROL_1:
+ case WM5100_DSP3_CONTROL_2:
+ case WM5100_DSP3_CONTROL_3:
+ case WM5100_DSP3_CONTROL_4:
+ case WM5100_DSP3_CONTROL_5:
+ case WM5100_DSP3_CONTROL_6:
+ case WM5100_DSP3_CONTROL_7:
+ case WM5100_DSP3_CONTROL_8:
+ case WM5100_DSP3_CONTROL_9:
+ case WM5100_DSP3_CONTROL_10:
+ case WM5100_DSP3_CONTROL_11:
+ case WM5100_DSP3_CONTROL_12:
+ case WM5100_DSP3_CONTROL_13:
+ case WM5100_DSP3_CONTROL_14:
+ case WM5100_DSP3_CONTROL_15:
+ case WM5100_DSP3_CONTROL_16:
+ case WM5100_DSP3_CONTROL_17:
+ case WM5100_DSP3_CONTROL_18:
+ case WM5100_DSP3_CONTROL_19:
+ case WM5100_DSP3_CONTROL_20:
+ case WM5100_DSP3_CONTROL_21:
+ case WM5100_DSP3_CONTROL_22:
+ case WM5100_DSP3_CONTROL_23:
+ case WM5100_DSP3_CONTROL_24:
+ case WM5100_DSP3_CONTROL_25:
+ case WM5100_DSP3_CONTROL_26:
+ case WM5100_DSP3_CONTROL_27:
+ case WM5100_DSP3_CONTROL_28:
+ case WM5100_DSP3_CONTROL_29:
+ case WM5100_DSP3_CONTROL_30:
return 1;
default:
- return 0;
+ if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+ (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+ (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+ (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+ (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+ (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+ (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+ (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+ (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+ return 1;
+ else
+ return 0;
}
}
@@ -1361,4 +1473,13 @@ struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT] = {
{ 0x0EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
{ 0x0ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
{ 0x0ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x0F02, 0x0000 }, /* R3842 - DSP1 Control 2 */
+ { 0x0F03, 0x0000 }, /* R3843 - DSP1 Control 3 */
+ { 0x0F04, 0x0000 }, /* R3844 - DSP1 Control 4 */
+ { 0x1002, 0x0000 }, /* R4098 - DSP2 Control 2 */
+ { 0x1003, 0x0000 }, /* R4099 - DSP2 Control 3 */
+ { 0x1004, 0x0000 }, /* R4100 - DSP2 Control 4 */
+ { 0x1102, 0x0000 }, /* R4354 - DSP3 Control 2 */
+ { 0x1103, 0x0000 }, /* R4355 - DSP3 Control 3 */
+ { 0x1104, 0x0000 }, /* R4356 - DSP3 Control 4 */
};
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index b9c185ce64e..cb6d5372103 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1265,29 +1265,12 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = {
{ WM5100_AUDIO_IF_3_19, 1 },
};
-static int wm5100_dai_to_base(struct snd_soc_dai *dai)
-{
- switch (dai->id) {
- case 0:
- return WM5100_AUDIO_IF_1_1 - 1;
- case 1:
- return WM5100_AUDIO_IF_2_1 - 1;
- case 2:
- return WM5100_AUDIO_IF_3_1 - 1;
- default:
- BUG();
- return -EINVAL;
- }
-}
-
static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
int lrclk, bclk, mask, base;
- base = wm5100_dai_to_base(dai);
- if (base < 0)
- return base;
+ base = dai->driver->base;
lrclk = 0;
bclk = 0;
@@ -1414,9 +1397,7 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
int i, base, bclk, aif_rate, lrclk, wl, fl, sr;
int *bclk_rates;
- base = wm5100_dai_to_base(dai);
- if (base < 0)
- return base;
+ base = dai->driver->base;
/* Data sizes if not using TDM */
wl = snd_pcm_format_width(params_format(params));
@@ -1897,6 +1878,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
static struct snd_soc_dai_driver wm5100_dai[] = {
{
.name = "wm5100-aif1",
+ .base = WM5100_AUDIO_IF_1_1 - 1,
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 2,
@@ -1916,6 +1898,7 @@ static struct snd_soc_dai_driver wm5100_dai[] = {
{
.name = "wm5100-aif2",
.id = 1,
+ .base = WM5100_AUDIO_IF_2_1 - 1,
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 2,
@@ -1935,6 +1918,7 @@ static struct snd_soc_dai_driver wm5100_dai[] = {
{
.name = "wm5100-aif3",
.id = 2,
+ .base = WM5100_AUDIO_IF_3_1 - 1,
.playback = {
.stream_name = "AIF3 Playback",
.channels_min = 2,
@@ -2454,7 +2438,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
wm5100->dev = &i2c->dev;
- wm5100->regmap = regmap_init_i2c(i2c, &wm5100_regmap);
+ wm5100->regmap = devm_regmap_init_i2c(i2c, &wm5100_regmap);
if (IS_ERR(wm5100->regmap)) {
ret = PTR_ERR(wm5100->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2479,7 +2463,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
ret);
- goto err_regmap;
+ goto err;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
@@ -2487,7 +2471,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable core supplies: %d\n",
ret);
- goto err_regmap;
+ goto err;
}
if (wm5100->pdata.ldo_ena) {
@@ -2660,8 +2644,6 @@ err_ldo:
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
-err_regmap:
- regmap_exit(wm5100->regmap);
err:
return ret;
}
@@ -2682,7 +2664,6 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c)
gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
gpio_free(wm5100->pdata.ldo_ena);
}
- regmap_exit(wm5100->regmap);
return 0;
}
@@ -2749,17 +2730,7 @@ static struct i2c_driver wm5100_i2c_driver = {
.id_table = wm5100_i2c_id,
};
-static int __init wm5100_modinit(void)
-{
- return i2c_add_driver(&wm5100_i2c_driver);
-}
-module_init(wm5100_modinit);
-
-static void __exit wm5100_exit(void)
-{
- i2c_del_driver(&wm5100_i2c_driver);
-}
-module_exit(wm5100_exit);
+module_i2c_driver(wm5100_i2c_driver);
MODULE_DESCRIPTION("ASoC WM5100 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/sound/soc/codecs/wm5100.h b/sound/soc/codecs/wm5100.h
index 25cb6016f9d..935a9b7fb27 100644
--- a/sound/soc/codecs/wm5100.h
+++ b/sound/soc/codecs/wm5100.h
@@ -709,6 +709,96 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
#define WM5100_HPLPF3_2 0xEC9
#define WM5100_HPLPF4_1 0xECC
#define WM5100_HPLPF4_2 0xECD
+#define WM5100_DSP1_CONTROL_1 0xF00
+#define WM5100_DSP1_CONTROL_2 0xF02
+#define WM5100_DSP1_CONTROL_3 0xF03
+#define WM5100_DSP1_CONTROL_4 0xF04
+#define WM5100_DSP1_CONTROL_5 0xF06
+#define WM5100_DSP1_CONTROL_6 0xF07
+#define WM5100_DSP1_CONTROL_7 0xF08
+#define WM5100_DSP1_CONTROL_8 0xF09
+#define WM5100_DSP1_CONTROL_9 0xF0A
+#define WM5100_DSP1_CONTROL_10 0xF0B
+#define WM5100_DSP1_CONTROL_11 0xF0C
+#define WM5100_DSP1_CONTROL_12 0xF0D
+#define WM5100_DSP1_CONTROL_13 0xF0F
+#define WM5100_DSP1_CONTROL_14 0xF10
+#define WM5100_DSP1_CONTROL_15 0xF11
+#define WM5100_DSP1_CONTROL_16 0xF12
+#define WM5100_DSP1_CONTROL_17 0xF13
+#define WM5100_DSP1_CONTROL_18 0xF14
+#define WM5100_DSP1_CONTROL_19 0xF16
+#define WM5100_DSP1_CONTROL_20 0xF17
+#define WM5100_DSP1_CONTROL_21 0xF18
+#define WM5100_DSP1_CONTROL_22 0xF1A
+#define WM5100_DSP1_CONTROL_23 0xF1B
+#define WM5100_DSP1_CONTROL_24 0xF1C
+#define WM5100_DSP1_CONTROL_25 0xF1E
+#define WM5100_DSP1_CONTROL_26 0xF20
+#define WM5100_DSP1_CONTROL_27 0xF21
+#define WM5100_DSP1_CONTROL_28 0xF22
+#define WM5100_DSP1_CONTROL_29 0xF23
+#define WM5100_DSP1_CONTROL_30 0xF24
+#define WM5100_DSP2_CONTROL_1 0x1000
+#define WM5100_DSP2_CONTROL_2 0x1002
+#define WM5100_DSP2_CONTROL_3 0x1003
+#define WM5100_DSP2_CONTROL_4 0x1004
+#define WM5100_DSP2_CONTROL_5 0x1006
+#define WM5100_DSP2_CONTROL_6 0x1007
+#define WM5100_DSP2_CONTROL_7 0x1008
+#define WM5100_DSP2_CONTROL_8 0x1009
+#define WM5100_DSP2_CONTROL_9 0x100A
+#define WM5100_DSP2_CONTROL_10 0x100B
+#define WM5100_DSP2_CONTROL_11 0x100C
+#define WM5100_DSP2_CONTROL_12 0x100D
+#define WM5100_DSP2_CONTROL_13 0x100F
+#define WM5100_DSP2_CONTROL_14 0x1010
+#define WM5100_DSP2_CONTROL_15 0x1011
+#define WM5100_DSP2_CONTROL_16 0x1012
+#define WM5100_DSP2_CONTROL_17 0x1013
+#define WM5100_DSP2_CONTROL_18 0x1014
+#define WM5100_DSP2_CONTROL_19 0x1016
+#define WM5100_DSP2_CONTROL_20 0x1017
+#define WM5100_DSP2_CONTROL_21 0x1018
+#define WM5100_DSP2_CONTROL_22 0x101A
+#define WM5100_DSP2_CONTROL_23 0x101B
+#define WM5100_DSP2_CONTROL_24 0x101C
+#define WM5100_DSP2_CONTROL_25 0x101E
+#define WM5100_DSP2_CONTROL_26 0x1020
+#define WM5100_DSP2_CONTROL_27 0x1021
+#define WM5100_DSP2_CONTROL_28 0x1022
+#define WM5100_DSP2_CONTROL_29 0x1023
+#define WM5100_DSP2_CONTROL_30 0x1024
+#define WM5100_DSP3_CONTROL_1 0x1100
+#define WM5100_DSP3_CONTROL_2 0x1102
+#define WM5100_DSP3_CONTROL_3 0x1103
+#define WM5100_DSP3_CONTROL_4 0x1104
+#define WM5100_DSP3_CONTROL_5 0x1106
+#define WM5100_DSP3_CONTROL_6 0x1107
+#define WM5100_DSP3_CONTROL_7 0x1108
+#define WM5100_DSP3_CONTROL_8 0x1109
+#define WM5100_DSP3_CONTROL_9 0x110A
+#define WM5100_DSP3_CONTROL_10 0x110B
+#define WM5100_DSP3_CONTROL_11 0x110C
+#define WM5100_DSP3_CONTROL_12 0x110D
+#define WM5100_DSP3_CONTROL_13 0x110F
+#define WM5100_DSP3_CONTROL_14 0x1110
+#define WM5100_DSP3_CONTROL_15 0x1111
+#define WM5100_DSP3_CONTROL_16 0x1112
+#define WM5100_DSP3_CONTROL_17 0x1113
+#define WM5100_DSP3_CONTROL_18 0x1114
+#define WM5100_DSP3_CONTROL_19 0x1116
+#define WM5100_DSP3_CONTROL_20 0x1117
+#define WM5100_DSP3_CONTROL_21 0x1118
+#define WM5100_DSP3_CONTROL_22 0x111A
+#define WM5100_DSP3_CONTROL_23 0x111B
+#define WM5100_DSP3_CONTROL_24 0x111C
+#define WM5100_DSP3_CONTROL_25 0x111E
+#define WM5100_DSP3_CONTROL_26 0x1120
+#define WM5100_DSP3_CONTROL_27 0x1121
+#define WM5100_DSP3_CONTROL_28 0x1122
+#define WM5100_DSP3_CONTROL_29 0x1123
+#define WM5100_DSP3_CONTROL_30 0x1124
#define WM5100_DSP1_DM_0 0x4000
#define WM5100_DSP1_DM_1 0x4001
#define WM5100_DSP1_DM_2 0x4002
@@ -4561,6 +4651,75 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
#define WM5100_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */
/*
+ * R4132 (0x1024) - DSP2 Control 30
+ */
+#define WM5100_DSP2_RATE_MASK 0xC000 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_SHIFT 14 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_WIDTH 2 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_START 0x0001 /* DSP2_START */
+#define WM5100_DSP2_START_MASK 0x0001 /* DSP2_START */
+#define WM5100_DSP2_START_SHIFT 0 /* DSP2_START */
+#define WM5100_DSP2_START_WIDTH 1 /* DSP2_START */
+
+/*
+ * R3876 (0xF24) - DSP1 Control 30
+ */
+#define WM5100_DSP1_RATE_MASK 0xC000 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_SHIFT 14 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_WIDTH 2 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_START 0x0001 /* DSP1_START */
+#define WM5100_DSP1_START_MASK 0x0001 /* DSP1_START */
+#define WM5100_DSP1_START_SHIFT 0 /* DSP1_START */
+#define WM5100_DSP1_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * R4388 (0x1124) - DSP3 Control 30
+ */
+#define WM5100_DSP3_RATE_MASK 0xC000 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_SHIFT 14 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_WIDTH 2 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_DBG_CLK_ENA 0x0008 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_MASK 0x0008 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_SHIFT 3 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_WIDTH 1 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_SYS_ENA 0x0004 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_MASK 0x0004 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_SHIFT 2 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_WIDTH 1 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_CORE_ENA 0x0002 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_MASK 0x0002 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_SHIFT 1 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_WIDTH 1 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_START 0x0001 /* DSP3_START */
+#define WM5100_DSP3_START_MASK 0x0001 /* DSP3_START */
+#define WM5100_DSP3_START_SHIFT 0 /* DSP3_START */
+#define WM5100_DSP3_START_WIDTH 1 /* DSP3_START */
+
+/*
* R16384 (0x4000) - DSP1 DM 0
*/
#define WM5100_DSP1_DM_START_1_MASK 0x00FF /* DSP1_DM_START - [7:0] */
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index aa12c6b6bee..555ee146ae0 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -71,13 +71,6 @@ struct wm8350_data {
int fll_freq_in;
};
-static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct wm8350 *wm8350 = codec->control_data;
- return wm8350->reg_cache[reg];
-}
-
static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -99,7 +92,7 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
{
struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out1 = &wm8350_data->out1;
- struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
int left_complete = 0, right_complete = 0;
u16 reg, val;
@@ -165,7 +158,7 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
{
struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out2 = &wm8350_data->out2;
- struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
int left_complete = 0, right_complete = 0;
u16 reg, val;
@@ -360,8 +353,8 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
return ret;
/* now hit the volume update bits (always bit 8) */
- val = wm8350_codec_read(codec, reg);
- wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+ val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, val | WM8350_OUT1_VU);
return 1;
}
@@ -781,7 +774,8 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
u16 fll_4;
switch (clk_id) {
@@ -795,9 +789,9 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
case WM8350_MCLK_SEL_PLL_32K:
wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
WM8350_MCLK_SEL);
- fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
~WM8350_FLL_CLK_SRC_MASK;
- wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+ snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
break;
}
@@ -819,39 +813,39 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
switch (div_id) {
case WM8350_ADC_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+ val = snd_soc_read(codec, WM8350_ADC_DIVIDER) &
~WM8350_ADC_CLKDIV_MASK;
- wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+ snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div);
break;
case WM8350_DAC_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+ val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) &
~WM8350_DAC_CLKDIV_MASK;
- wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+ snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
break;
case WM8350_BCLK_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
~WM8350_BCLK_DIV_MASK;
- wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_OPCLK_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
~WM8350_OPCLK_DIV_MASK;
- wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_SYS_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
~WM8350_MCLK_DIV_MASK;
- wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
break;
case WM8350_DACLR_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ val = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
~WM8350_DACLRC_RATE_MASK;
- wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+ snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div);
break;
case WM8350_ADCLR_CLKDIV:
- val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ val = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
~WM8350_ADCLRC_RATE_MASK;
- wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+ snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div);
break;
default:
return -EINVAL;
@@ -863,13 +857,13 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
- u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+ u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) &
~WM8350_BCLK_MSTR;
- u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
~WM8350_DACLRC_ENA;
- u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
~WM8350_ADCLRC_ENA;
/* set master/slave audio interface */
@@ -922,42 +916,10 @@ static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
- wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
- wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
- wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
- return 0;
-}
-
-static int wm8350_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *codec_dai)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
- int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
- WM8350_BCLK_MSTR;
- int enabled = 0;
-
- /* Check that the DACs or ADCs are enabled since they are
- * required for LRC in master mode. The DACs or ADCs need a
- * valid audio path i.e. pin -> ADC or DAC -> pin before
- * the LRC will be enabled in master mode. */
- if (!master || cmd != SNDRV_PCM_TRIGGER_START)
- return 0;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
- (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
- } else {
- enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
- (WM8350_DACR_ENA | WM8350_DACL_ENA);
- }
-
- if (!enabled) {
- dev_err(codec->dev,
- "%s: invalid audio path - no clocks available\n",
- __func__);
- return -EINVAL;
- }
+ snd_soc_write(codec, WM8350_AI_FORMATING, iface);
+ snd_soc_write(codec, WM8350_AI_DAC_CONTROL, master);
+ snd_soc_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+ snd_soc_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
return 0;
}
@@ -966,8 +928,9 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8350 *wm8350 = codec->control_data;
- u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
+ u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
~WM8350_AIF_WL_MASK;
/* bit size */
@@ -985,7 +948,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+ snd_soc_write(codec, WM8350_AI_FORMATING, iface);
/* The sloping stopband filter is recommended for use with
* lower sample rates to improve performance.
@@ -1005,12 +968,15 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8350_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8350 *wm8350 = codec->control_data;
+ unsigned int val;
if (mute)
- wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ val = WM8350_DAC_MUTE_ENA;
else
- wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ val = 0;
+
+ snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
+
return 0;
}
@@ -1079,8 +1045,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8350 *wm8350 = codec->control_data;
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
struct _fll_div fll_div;
int ret = 0;
u16 fll_1, fll_4;
@@ -1104,17 +1070,17 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
fll_div.ratio);
/* set up N.K & dividers */
- fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+ fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) &
~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
- wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+ snd_soc_write(codec, WM8350_FLL_CONTROL_1,
fll_1 | (fll_div.div << 8) | 0x50);
- wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+ snd_soc_write(codec, WM8350_FLL_CONTROL_2,
(fll_div.ratio << 11) | (fll_div.
n & WM8350_FLL_N_MASK));
- wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
- fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ snd_soc_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+ fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
- wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+ snd_soc_write(codec, WM8350_FLL_CONTROL_4,
fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
(fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
@@ -1131,8 +1097,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
static int wm8350_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct wm8350 *wm8350 = codec->control_data;
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
struct wm8350_audio_platform_data *platform =
wm8350->codec.platform_data;
u16 pm1;
@@ -1339,35 +1305,36 @@ static void wm8350_hpr_work(struct work_struct *work)
wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL);
}
-static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
+static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
{
struct wm8350_data *priv = data;
struct wm8350 *wm8350 = priv->wm8350;
- struct wm8350_jack_data *jack = NULL;
- switch (irq - wm8350->irq_base) {
- case WM8350_IRQ_CODEC_JCK_DET_L:
#ifndef CONFIG_SND_SOC_WM8350_MODULE
- trace_snd_soc_jack_irq("WM8350 HPL");
+ trace_snd_soc_jack_irq("WM8350 HPL");
#endif
- jack = &priv->hpl;
- break;
- case WM8350_IRQ_CODEC_JCK_DET_R:
+ if (device_may_wakeup(wm8350->dev))
+ pm_wakeup_event(wm8350->dev, 250);
+
+ schedule_delayed_work(&priv->hpl.work, 200);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ struct wm8350 *wm8350 = priv->wm8350;
+
#ifndef CONFIG_SND_SOC_WM8350_MODULE
- trace_snd_soc_jack_irq("WM8350 HPR");
+ trace_snd_soc_jack_irq("WM8350 HPR");
#endif
- jack = &priv->hpr;
- break;
-
- default:
- BUG();
- }
if (device_may_wakeup(wm8350->dev))
pm_wakeup_event(wm8350->dev, 250);
- schedule_delayed_work(&jack->work, 200);
+ schedule_delayed_work(&priv->hpr.work, 200);
return IRQ_HANDLED;
}
@@ -1387,7 +1354,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
struct snd_soc_jack *jack, int report)
{
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
- struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350 *wm8350 = priv->wm8350;
int irq;
int ena;
@@ -1418,7 +1385,14 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
}
/* Sync status */
- wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
+ switch (which) {
+ case WM8350_JDL:
+ wm8350_hpl_jack_handler(0, priv);
+ break;
+ case WM8350_JDR:
+ wm8350_hpr_jack_handler(0, priv);
+ break;
+ }
return 0;
}
@@ -1463,7 +1437,7 @@ int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
int detect_report, int short_report)
{
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
- struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350 *wm8350 = priv->wm8350;
priv->mic.jack = jack;
priv->mic.report = detect_report;
@@ -1491,7 +1465,6 @@ EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
static const struct snd_soc_dai_ops wm8350_dai_ops = {
.hw_params = wm8350_pcm_hw_params,
.digital_mute = wm8350_mute,
- .trigger = wm8350_pcm_trigger,
.set_fmt = wm8350_set_dai_fmt,
.set_sysclk = wm8350_set_dai_sysclk,
.set_pll = wm8350_set_fll,
@@ -1559,9 +1532,9 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec)
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
/* Enable robust clocking mode in ADC */
- wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
- wm8350_codec_write(codec, 0xde, 0x13);
- wm8350_codec_write(codec, WM8350_SECURITY, 0);
+ snd_soc_write(codec, WM8350_SECURITY, 0xa7);
+ snd_soc_write(codec, 0xde, 0x13);
+ snd_soc_write(codec, WM8350_SECURITY, 0);
/* read OUT1 & OUT2 volumes */
out1 = &priv->out1;
@@ -1601,10 +1574,10 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec)
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
- wm8350_hp_jack_handler, 0, "Left jack detect",
+ wm8350_hpl_jack_handler, 0, "Left jack detect",
priv);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
- wm8350_hp_jack_handler, 0, "Right jack detect",
+ wm8350_hpr_jack_handler, 0, "Right jack detect",
priv);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
wm8350_mic_handler, 0, "Microphone short", priv);
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 898979d2301..5dc31ebcd0e 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -138,8 +138,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
return ret;
/* now hit the volume update bits (always bit 8) */
- val = wm8400_read(codec, reg);
- return wm8400_write(codec, reg, val | 0x0100);
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
}
#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
@@ -362,8 +362,8 @@ static int inmixer_event (struct snd_soc_dapm_widget *w,
{
u16 reg, fakepower;
- reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2);
- fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS);
+ reg = snd_soc_read(w->codec, WM8400_POWER_MANAGEMENT_2);
+ fakepower = snd_soc_read(w->codec, WM8400_INTDRIVBITS);
if (fakepower & ((1 << WM8400_INMIXL_PWR) |
(1 << WM8400_AINLMUX_PWR))) {
@@ -378,7 +378,7 @@ static int inmixer_event (struct snd_soc_dapm_widget *w,
} else {
reg &= ~WM8400_AINR_ENA;
}
- wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
+ snd_soc_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
return 0;
}
@@ -394,7 +394,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
switch (reg_shift) {
case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) :
- reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1);
+ reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER1);
if (reg & WM8400_LDLO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -402,7 +402,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
}
break;
case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8):
- reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2);
+ reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER2);
if (reg & WM8400_RDRO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -410,7 +410,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
}
break;
case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8):
- reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+ reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER);
if (reg & WM8400_LDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -418,7 +418,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
}
break;
case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8):
- reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+ reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER);
if (reg & WM8400_RDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -1021,13 +1021,13 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
wm8400->fll_in = freq_in;
/* We *must* disable the FLL before any changes */
- reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2);
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2);
reg &= ~WM8400_FLL_ENA;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
- reg = wm8400_read(codec, WM8400_FLL_CONTROL_1);
+ reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1);
reg &= ~WM8400_FLL_OSC_ENA;
- wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
if (!freq_out)
return 0;
@@ -1035,15 +1035,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
reg |= WM8400_FLL_FRAC | factors.fratio;
reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT;
- wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
- wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k);
- wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_2, factors.k);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_3, factors.n);
- reg = wm8400_read(codec, WM8400_FLL_CONTROL_4);
+ reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4);
reg &= ~WM8400_FLL_OUTDIV_MASK;
reg |= factors.outdiv;
- wm8400_write(codec, WM8400_FLL_CONTROL_4, reg);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg);
return 0;
}
@@ -1057,8 +1057,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
u16 audio1, audio3;
- audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
- audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3);
+ audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1099,8 +1099,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
- wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
return 0;
}
@@ -1112,24 +1112,24 @@ static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8400_MCLK_DIV:
- reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
~WM8400_MCLK_DIV_MASK;
- wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_DACCLK_DIV:
- reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
~WM8400_DAC_CLKDIV_MASK;
- wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_ADCCLK_DIV:
- reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
~WM8400_ADC_CLKDIV_MASK;
- wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
break;
case WM8400_BCLK_DIV:
- reg = wm8400_read(codec, WM8400_CLOCKING_1) &
+ reg = snd_soc_read(codec, WM8400_CLOCKING_1) &
~WM8400_BCLK_DIV_MASK;
- wm8400_write(codec, WM8400_CLOCKING_1, reg | div);
+ snd_soc_write(codec, WM8400_CLOCKING_1, reg | div);
break;
default:
return -EINVAL;
@@ -1145,9 +1145,8 @@ static int wm8400_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;
- u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
+ struct snd_soc_codec *codec = dai->codec;
+ u16 audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
audio1 &= ~WM8400_AIF_WL_MASK;
/* bit size */
@@ -1165,19 +1164,19 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
return 0;
}
static int wm8400_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
+ u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
if (mute)
- wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+ snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
else
- wm8400_write(codec, WM8400_DAC_CTRL, val);
+ snd_soc_write(codec, WM8400_DAC_CTRL, val);
return 0;
}
@@ -1196,9 +1195,9 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*50k */
- val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
~WM8400_VMID_MODE_MASK;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
break;
case SND_SOC_BIAS_STANDBY:
@@ -1212,74 +1211,74 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
WM8400_CODEC_ENA | WM8400_SYSCLK_ENA);
/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
- wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_BUFDCOPEN | WM8400_POBCTRL);
msleep(50);
/* Enable VREF & VMID at 2x50k */
- val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
val |= 0x2 | WM8400_VREF_ENA;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
/* Enable BUFIOEN */
- wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_BUFDCOPEN | WM8400_POBCTRL |
WM8400_BUFIOEN);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
}
/* VMID=2*300k */
- val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
~WM8400_VMID_MODE_MASK;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
break;
case SND_SOC_BIAS_OFF:
/* Enable POBCTRL and SOFT_ST */
- wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_POBCTRL | WM8400_BUFIOEN);
/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
WM8400_BUFDCOPEN | WM8400_POBCTRL |
WM8400_BUFIOEN);
/* mute DAC */
- val = wm8400_read(codec, WM8400_DAC_CTRL);
- wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+ val = snd_soc_read(codec, WM8400_DAC_CTRL);
+ snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
/* Enable any disabled outputs */
- val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
WM8400_OUT4_ENA | WM8400_LOUT_ENA |
WM8400_ROUT_ENA;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
/* Disable VMID */
val &= ~WM8400_VMID_MODE_MASK;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
msleep(300);
/* Enable all output discharge bits */
- wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
+ snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
WM8400_DIS_ROUT);
/* Disable VREF */
val &= ~WM8400_VREF_ENA;
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8400_write(codec, WM8400_ANTIPOP2, 0x0);
+ snd_soc_write(codec, WM8400_ANTIPOP2, 0x0);
ret = regulator_bulk_disable(ARRAY_SIZE(power),
&power[0]);
@@ -1385,19 +1384,19 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
wm8400_codec_reset(codec);
- reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
/* Latch volume update bits */
- reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
- wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
reg & WM8400_IPVU);
- reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
- wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
reg & WM8400_IPVU);
- wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
- wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
if (!schedule_work(&priv->work)) {
ret = -EINVAL;
@@ -1414,8 +1413,8 @@ static int wm8400_codec_remove(struct snd_soc_codec *codec)
{
u16 reg;
- reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
- wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
reg & (~WM8400_CODEC_ENA));
regulator_bulk_free(ARRAY_SIZE(power), power);
@@ -1428,7 +1427,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
.remove = wm8400_codec_remove,
.suspend = wm8400_suspend,
.resume = wm8400_resume,
- .read = wm8400_read,
+ .read = snd_soc_read,
.write = wm8400_write,
.set_bias_level = wm8400_set_bias_level,
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 9166126bd31..56a049555e2 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -392,8 +392,7 @@ static int wm8510_pcm_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 snd_soc_codec *codec = dai->codec;
u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 7fea2c3bf7e..1c3ffb290cd 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -145,8 +145,7 @@ static int wm8523_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 snd_soc_codec *codec = dai->codec;
struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
int i;
u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index fc3d59e4908..1467f97dce2 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -88,8 +88,7 @@ static int wm8728_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 snd_soc_codec *codec = dai->codec;
u16 dac = snd_soc_read(codec, WM8728_DACCTL);
dac &= ~0x18;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index a32caa72bd7..9d1b9b0271f 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -635,16 +635,17 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi)
struct wm8731_priv *wm8731;
int ret;
- wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+ wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv),
+ GFP_KERNEL);
if (wm8731 == NULL)
return -ENOMEM;
- wm8731->regmap = regmap_init_spi(spi, &wm8731_regmap);
+ wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
if (IS_ERR(wm8731->regmap)) {
ret = PTR_ERR(wm8731->regmap);
dev_err(&spi->dev, "Failed to allocate register map: %d\n",
ret);
- goto err;
+ return ret;
}
spi_set_drvdata(spi, wm8731);
@@ -653,25 +654,15 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi)
&soc_codec_dev_wm8731, &wm8731_dai, 1);
if (ret != 0) {
dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
- goto err_regmap;
+ return ret;
}
return 0;
-
-err_regmap:
- regmap_exit(wm8731->regmap);
-err:
- kfree(wm8731);
- return ret;
}
static int __devexit wm8731_spi_remove(struct spi_device *spi)
{
- struct wm8731_priv *wm8731 = spi_get_drvdata(spi);
-
snd_soc_unregister_codec(&spi->dev);
- regmap_exit(wm8731->regmap);
- kfree(wm8731);
return 0;
}
@@ -693,16 +684,17 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c,
struct wm8731_priv *wm8731;
int ret;
- wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+ wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+ GFP_KERNEL);
if (wm8731 == NULL)
return -ENOMEM;
- wm8731->regmap = regmap_init_i2c(i2c, &wm8731_regmap);
+ wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
if (IS_ERR(wm8731->regmap)) {
ret = PTR_ERR(wm8731->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
- goto err;
+ return ret;
}
i2c_set_clientdata(i2c, wm8731);
@@ -711,24 +703,15 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c,
&soc_codec_dev_wm8731, &wm8731_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
- goto err_regmap;
+ return ret;
}
return 0;
-
-err_regmap:
- regmap_exit(wm8731->regmap);
-err:
- kfree(wm8731);
- return ret;
}
static __devexit int wm8731_i2c_remove(struct i2c_client *client)
{
- struct wm8731_priv *wm8731 = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev);
- regmap_exit(wm8731->regmap);
- kfree(wm8731);
return 0;
}
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 4fe9d191e27..d0520124616 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -329,8 +329,7 @@ static int wm8737_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 snd_soc_codec *codec = dai->codec;
struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
int i;
u16 clocking = 0;
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 3941f50bf18..6e849cb0424 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -203,8 +203,7 @@ static int wm8741_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 snd_soc_codec *codec = dai->codec;
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
int i;
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index e4c50ce7d9c..89151ca5e77 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -547,8 +547,7 @@ static int wm8750_pcm_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 snd_soc_codec *codec = dai->codec;
struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index e27e7b62b36..a26482cd765 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -931,8 +931,7 @@ static int wm8753_pcm_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 snd_soc_codec *codec = dai->codec;
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01f3;
u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x017f;
@@ -1161,8 +1160,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x01c0;
u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01f3;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index f18c554efc9..077c9628c70 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -610,8 +610,7 @@ static int wm8900_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 snd_soc_codec *codec = dai->codec;
u16 reg;
reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index c91fb2f99c1..86b8a292659 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1432,8 +1432,7 @@ static int wm8903_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 snd_soc_codec *codec = dai->codec;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
int fs = params_rate(params);
int bclk;
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index d2883affea3..481a3d9cfe4 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -371,8 +371,7 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
u16 companding = snd_soc_read(codec,
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 840d72086d0..8bc659d8dd2 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -505,8 +505,7 @@ static int wm8960_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 snd_soc_codec *codec = dai->codec;
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
int i;
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 15d467ff91b..0cfce9999c8 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1478,7 +1478,8 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
{
- return 0;
+ return regcache_sync_region(codec->control_data,
+ WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER);
}
static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
@@ -1755,10 +1756,22 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+
+SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
+
+SOC_SINGLE("DRC Switch", WM8962_DRC_1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DRC Coefficients", WM8962_DRC_1, 5, WM8962_DRC_ENA),
+
WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
+SND_SOC_BYTES("VSS Coefficients", WM8962_VSS_XHD2_1, 148),
WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
+SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1),
WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
+SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30),
};
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2519,8 +2532,7 @@ static int wm8962_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 snd_soc_codec *codec = dai->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int i;
int aif0 = 0;
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 28fe59e3ce0..eef783f6b6d 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -478,8 +478,7 @@ static int wm8971_pcm_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 snd_soc_codec *codec = dai->codec;
struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 72d5fdcd3cc..a5be3adecf7 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -723,8 +723,7 @@ static int wm8978_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 snd_soc_codec *codec = dai->codec;
struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
/* Word length mask = 0x60 */
u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 6cdf6a2bc28..1d4c5cf47b0 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -668,8 +668,7 @@ static int wm8988_pcm_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 snd_soc_codec *codec = dai->codec;
struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 9d242351e6e..db63c97ddf5 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1112,8 +1112,7 @@ static int wm8990_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 snd_soc_codec *codec = dai->codec;
u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
audio1 &= ~WM8990_AIF_WL_MASK;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d256a934064..36acfccab99 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -218,7 +218,6 @@ struct wm8993_priv {
unsigned int sysclk_rate;
unsigned int fs;
unsigned int bclk;
- int class_w_users;
unsigned int fll_fref;
unsigned int fll_fout;
int fll_src;
@@ -824,84 +823,6 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
return 0;
}
-/*
- * When used with DAC outputs only the WM8993 charge pump supports
- * operation in class W mode, providing very low power consumption
- * when used with digital sources. Enable and disable this mode
- * automatically depending on the mixer configuration.
- *
- * Currently the only supported paths are the direct DAC->headphone
- * paths (which provide minimum power consumption anyway).
- */
-static int class_w_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
- struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- /* Turn it off if we're using the main output mixer */
- if (ucontrol->value.integer.value[0] == 0) {
- if (wm8993->class_w_users == 0) {
- dev_dbg(codec->dev, "Disabling Class W\n");
- snd_soc_update_bits(codec, WM8993_CLASS_W_0,
- WM8993_CP_DYN_FREQ |
- WM8993_CP_DYN_V,
- 0);
- }
- wm8993->class_w_users++;
- wm8993->hubs_data.class_w = true;
- }
-
- /* Implement the change */
- ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
- /* Enable it if we're using the direct DAC path */
- if (ucontrol->value.integer.value[0] == 1) {
- if (wm8993->class_w_users == 1) {
- dev_dbg(codec->dev, "Enabling Class W\n");
- snd_soc_update_bits(codec, WM8993_CLASS_W_0,
- WM8993_CP_DYN_FREQ |
- WM8993_CP_DYN_V,
- WM8993_CP_DYN_FREQ |
- WM8993_CP_DYN_V);
- }
- wm8993->class_w_users--;
- wm8993->hubs_data.class_w = false;
- }
-
- dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
- wm8993->class_w_users);
-
- return ret;
-}
-
-#define SOC_DAPM_ENUM_W(xname, xenum) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_enum_double, \
- .get = snd_soc_dapm_get_enum_double, \
- .put = class_w_put, \
- .private_value = (unsigned long)&xenum }
-
-static const char *hp_mux_text[] = {
- "Mixer",
- "DAC",
-};
-
-static const struct soc_enum hpl_enum =
- SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
- SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
- SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
- SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
-
static const struct snd_kcontrol_new left_speaker_mixer[] = {
SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
@@ -988,8 +909,8 @@ SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux),
SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -1579,9 +1500,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
return ret;
}
- /* By default we're using the output mixers */
- wm8993->class_w_users = 2;
-
/* Latch volume update bits and default ZC on */
snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
WM8993_DAC_VU, WM8993_DAC_VU);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 2de12ebe43b..993639d694c 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -70,8 +70,8 @@ static const struct wm8958_micd_rate micdet_rates[] = {
static const struct wm8958_micd_rate jackdet_rates[] = {
{ 32768, true, 0, 1 },
{ 32768, false, 0, 1 },
- { 44100 * 256, true, 7, 10 },
- { 44100 * 256, false, 7, 10 },
+ { 44100 * 256, true, 10, 10 },
+ { 44100 * 256, false, 7, 8 },
};
static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
@@ -82,7 +82,8 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
const struct wm8958_micd_rate *rates;
int num_rates;
- if (wm8994->jack_cb != wm8958_default_micdet)
+ if (!(wm8994->pdata && wm8994->pdata->micd_rates) &&
+ wm8994->jack_cb != wm8958_default_micdet)
return;
idle = !wm8994->jack_mic;
@@ -118,6 +119,10 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
| rates[best].rate << WM8958_MICD_RATE_SHIFT;
+ dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n",
+ rates[best].start, rates[best].rate, sysclk,
+ idle ? "idle" : "active");
+
snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
WM8958_MICD_BIAS_STARTTIME_MASK |
WM8958_MICD_RATE_MASK, val);
@@ -398,7 +403,7 @@ static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
wm8994->dac_rates[iface]);
/* The EQ will be disabled while reconfiguring it, remember the
- * current configuration.
+ * current configuration.
*/
save = snd_soc_read(codec, base);
save &= WM8994_AIF1DAC1_EQ_ENA;
@@ -689,6 +694,9 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode)
if (!wm8994->jackdet || !wm8994->jack_cb)
return;
+ if (!wm8994->jackdet || !wm8994->jack_cb)
+ return;
+
if (wm8994->active_refcount)
mode = WM1811_JACKDET_MODE_AUDIO;
@@ -784,7 +792,7 @@ static void vmid_reference(struct snd_soc_codec *codec)
switch (wm8994->vmid_mode) {
default:
- WARN_ON(0 == "Invalid VMID mode");
+ WARN_ON(NULL == "Invalid VMID mode");
case WM8994_VMID_NORMAL:
/* Startup bias, VMID ramp & buffer */
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
@@ -937,27 +945,12 @@ static int vmid_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static void wm8994_update_class_w(struct snd_soc_codec *codec)
+static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- int enable = 1;
int source = 0; /* GCC flow analysis can't track enable */
int reg, reg_r;
- /* Only support direct DAC->headphone paths */
- reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
- if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
- dev_vdbg(codec->dev, "HPL connected to output mixer\n");
- enable = 0;
- }
-
- reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
- if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
- dev_vdbg(codec->dev, "HPR connected to output mixer\n");
- enable = 0;
- }
-
- /* We also need the same setting for L/R and only one path */
+ /* We also need the same AIF source for L/R and only one path */
reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
switch (reg) {
case WM8994_AIF2DACL_TO_DAC1L:
@@ -974,30 +967,20 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec)
break;
default:
dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
- enable = 0;
- break;
+ return false;
}
reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
if (reg_r != reg) {
dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
- enable = 0;
+ return false;
}
- if (enable) {
- dev_dbg(codec->dev, "Class W enabled\n");
- snd_soc_update_bits(codec, WM8994_CLASS_W_1,
- WM8994_CP_DYN_PWR |
- WM8994_CP_DYN_SRC_SEL_MASK,
- source | WM8994_CP_DYN_PWR);
- wm8994->hubs.class_w = true;
-
- } else {
- dev_dbg(codec->dev, "Class W disabled\n");
- snd_soc_update_bits(codec, WM8994_CLASS_W_1,
- WM8994_CP_DYN_PWR, 0);
- wm8994->hubs.class_w = false;
- }
+ /* Set the source up */
+ snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+ WM8994_CP_DYN_SRC_SEL_MASK, source);
+
+ return true;
}
static int aif1clk_ev(struct snd_soc_dapm_widget *w,
@@ -1280,45 +1263,6 @@ static int dac_ev(struct snd_soc_dapm_widget *w,
return 0;
}
-static const char *hp_mux_text[] = {
- "Mixer",
- "DAC",
-};
-
-#define WM8994_HP_ENUM(xname, xenum) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_enum_double, \
- .get = snd_soc_dapm_get_enum_double, \
- .put = wm8994_put_hp_enum, \
- .private_value = (unsigned long)&xenum }
-
-static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct snd_soc_codec *codec = w->codec;
- int ret;
-
- ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
- wm8994_update_class_w(codec);
-
- return ret;
-}
-
-static const struct soc_enum hpl_enum =
- SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
- WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
- SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
- WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
-
static const char *adc_mux_text[] = {
"ADC",
"DMIC",
@@ -1430,7 +1374,7 @@ static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
- wm8994_update_class_w(codec);
+ wm_hubs_update_class_w(codec);
return ret;
}
@@ -1524,7 +1468,7 @@ static const struct snd_kcontrol_new wm8958_aif3adc_mux =
SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum);
static const char *mono_pcm_out_text[] = {
- "None", "AIF2ADCL", "AIF2ADCR",
+ "None", "AIF2ADCL", "AIF2ADCR",
};
static const struct soc_enum mono_pcm_out_enum =
@@ -1573,9 +1517,9 @@ SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux,
+SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux,
+SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
@@ -1591,8 +1535,8 @@ SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
};
static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@@ -1732,6 +1676,7 @@ SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux),
};
static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0),
SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux),
SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux),
SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux),
@@ -1972,6 +1917,9 @@ static const struct snd_soc_dapm_route wm8958_intercon[] = {
{ "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" },
{ "AIF2DACR Mux", "AIF3", "AIF3DACDAT" },
+ { "AIF3DACDAT", NULL, "AIF3" },
+ { "AIF3ADCDAT", NULL, "AIF3" },
+
{ "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" },
{ "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" },
@@ -2068,24 +2016,20 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
struct wm8994 *control = wm8994->wm8994;
int reg_offset, ret;
struct fll_div fll;
- u16 reg, aif1, aif2;
+ u16 reg, clk1, aif_reg, aif_src;
unsigned long timeout;
bool was_enabled;
- aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
- & WM8994_AIF1CLK_ENA;
-
- aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
- & WM8994_AIF2CLK_ENA;
-
switch (id) {
case WM8994_FLL1:
reg_offset = 0;
id = 0;
+ aif_src = 0x10;
break;
case WM8994_FLL2:
reg_offset = 0x20;
id = 1;
+ aif_src = 0x18;
break;
default:
return -EINVAL;
@@ -2127,16 +2071,33 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
if (ret < 0)
return ret;
- /* Gate the AIF clocks while we reclock */
- snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
- WM8994_AIF1CLK_ENA, 0);
- snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
- WM8994_AIF2CLK_ENA, 0);
+ /* Make sure that we're not providing SYSCLK right now */
+ clk1 = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (clk1 & WM8994_SYSCLK_SRC)
+ aif_reg = WM8994_AIF2_CLOCKING_1;
+ else
+ aif_reg = WM8994_AIF1_CLOCKING_1;
+ reg = snd_soc_read(codec, aif_reg);
+
+ if ((reg & WM8994_AIF1CLK_ENA) &&
+ (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) {
+ dev_err(codec->dev, "FLL%d is currently providing SYSCLK\n",
+ id + 1);
+ return -EBUSY;
+ }
/* We always need to disable the FLL while reconfiguring */
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
WM8994_FLL1_ENA, 0);
+ if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
+ freq_in == freq_out && freq_out) {
+ dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1);
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8958_FLL1_BYP, WM8958_FLL1_BYP);
+ goto out;
+ }
+
reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
(fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
@@ -2151,6 +2112,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
fll.n << WM8994_FLL1_N_SHIFT);
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
WM8994_FLL1_REFCLK_SRC_MASK,
(fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
@@ -2213,16 +2175,11 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
}
}
+out:
wm8994->fll[id].in = freq_in;
wm8994->fll[id].out = freq_out;
wm8994->fll[id].src = src;
- /* Enable any gated AIF clocks */
- snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
- WM8994_AIF1CLK_ENA, aif1);
- snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
- WM8994_AIF2CLK_ENA, aif2);
-
configure_clock(codec);
return 0;
@@ -2290,7 +2247,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
case WM8994_SYSCLK_OPCLK:
/* Special case - a division (times 10) is given and
- * no effect on main clocking.
+ * no effect on main clocking.
*/
if (freq) {
for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
@@ -2792,33 +2749,6 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
}
-static void wm8994_aif_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- int rate_reg = 0;
-
- switch (dai->id) {
- case 1:
- rate_reg = WM8994_AIF1_RATE;
- break;
- case 2:
- rate_reg = WM8994_AIF2_RATE;
- break;
- default:
- break;
- }
-
- /* If the DAI is idle then configure the divider tree for the
- * lowest output rate to save a little power if the clock is
- * still active (eg, because it is system clock).
- */
- if (rate_reg && !dai->playback_active && !dai->capture_active)
- snd_soc_update_bits(codec, rate_reg,
- WM8994_AIF1_SR_MASK |
- WM8994_AIF1CLK_RATE_MASK, 0x9);
-}
-
static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -2860,10 +2790,6 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
reg = WM8994_AIF2_MASTER_SLAVE;
mask = WM8994_AIF2_TRI;
break;
- case 3:
- reg = WM8994_POWER_MANAGEMENT_6;
- mask = WM8994_AIF3_TRI;
- break;
default:
return -EINVAL;
}
@@ -2900,7 +2826,6 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
- .shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@@ -2910,7 +2835,6 @@ static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
- .shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@@ -2918,7 +2842,6 @@ static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
.hw_params = wm8994_aif3_hw_params,
- .set_tristate = wm8994_set_tristate,
};
static struct snd_soc_dai_driver wm8994_dai[] = {
@@ -3126,14 +3049,14 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
/* Expand the array... */
t = krealloc(wm8994->retune_mobile_texts,
- sizeof(char *) *
+ sizeof(char *) *
(wm8994->num_retune_mobile_texts + 1),
GFP_KERNEL);
if (t == NULL)
continue;
/* ...store the new entry... */
- t[wm8994->num_retune_mobile_texts] =
+ t[wm8994->num_retune_mobile_texts] =
pdata->retune_mobile_cfgs[i].name;
/* ...and remember the new version. */
@@ -3304,25 +3227,25 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
EXPORT_SYMBOL_GPL(wm8994_mic_detect);
-static irqreturn_t wm8994_mic_irq(int irq, void *data)
+static void wm8994_mic_work(struct work_struct *work)
{
- struct wm8994_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
- int reg;
+ struct wm8994_priv *priv = container_of(work,
+ struct wm8994_priv,
+ mic_work.work);
+ struct regmap *regmap = priv->wm8994->regmap;
+ struct device *dev = priv->wm8994->dev;
+ unsigned int reg;
+ int ret;
int report;
-#ifndef CONFIG_SND_SOC_WM8994_MODULE
- trace_snd_soc_jack_irq(dev_name(codec->dev));
-#endif
-
- reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2);
- if (reg < 0) {
- dev_err(codec->dev, "Failed to read microphone status: %d\n",
- reg);
- return IRQ_HANDLED;
+ ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read microphone status: %d\n",
+ ret);
+ return;
}
- dev_dbg(codec->dev, "Microphone status: %x\n", reg);
+ dev_dbg(dev, "Microphone status: %x\n", reg);
report = 0;
if (reg & WM8994_MIC1_DET_STS) {
@@ -3361,6 +3284,20 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
snd_soc_jack_report(priv->micdet[1].jack, report,
SND_JACK_HEADSET | SND_JACK_BTN_0);
+}
+
+static irqreturn_t wm8994_mic_irq(int irq, void *data)
+{
+ struct wm8994_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+
+#ifndef CONFIG_SND_SOC_WM8994_MODULE
+ trace_snd_soc_jack_irq(dev_name(codec->dev));
+#endif
+
+ pm_wakeup_event(codec->dev, 300);
+
+ schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
return IRQ_HANDLED;
}
@@ -3415,9 +3352,6 @@ static void wm8958_default_micdet(u16 status, void *data)
wm8958_micd_set_rate(codec);
- snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
- SND_JACK_HEADSET);
-
/* If we have jackdet that will detect removal */
if (wm8994->jackdet) {
mutex_lock(&wm8994->accdet_lock);
@@ -3430,14 +3364,13 @@ static void wm8958_default_micdet(u16 status, void *data)
mutex_unlock(&wm8994->accdet_lock);
- if (wm8994->pdata->jd_ext_cap) {
- mutex_lock(&codec->mutex);
+ if (wm8994->pdata->jd_ext_cap)
snd_soc_dapm_disable_pin(&codec->dapm,
"MICBIAS2");
- snd_soc_dapm_sync(&codec->dapm);
- mutex_unlock(&codec->mutex);
- }
}
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
}
/* Report short circuit as a button */
@@ -3489,6 +3422,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
if (present) {
dev_dbg(codec->dev, "Jack detected\n");
+ wm8958_micd_set_rate(codec);
+
snd_soc_update_bits(codec, WM8958_MICBIAS2,
WM8958_MICB2_DISCH, 0);
@@ -3526,16 +3461,11 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
/* If required for an external cap force MICBIAS on */
if (wm8994->pdata->jd_ext_cap) {
- mutex_lock(&codec->mutex);
-
if (present)
snd_soc_dapm_force_enable_pin(&codec->dapm,
"MICBIAS2");
else
snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
-
- snd_soc_dapm_sync(&codec->dapm);
- mutex_unlock(&codec->mutex);
}
if (present)
@@ -3740,6 +3670,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->codec = codec;
mutex_init(&wm8994->accdet_lock);
+ INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work);
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);
@@ -3783,13 +3714,22 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
case WM8958:
wm8994->hubs.dcs_readback_mode = 1;
wm8994->hubs.hp_startup_mode = 1;
+
+ switch (wm8994->revision) {
+ case 0:
+ break;
+ default:
+ wm8994->fll_byp = true;
+ break;
+ }
break;
case WM1811:
wm8994->hubs.dcs_readback_mode = 2;
wm8994->hubs.no_series_update = 1;
wm8994->hubs.hp_startup_mode = 1;
- wm8994->hubs.no_cache_class_w = true;
+ wm8994->hubs.no_cache_dac_hp_direct = true;
+ wm8994->fll_byp = true;
switch (wm8994->revision) {
case 0:
@@ -4010,7 +3950,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
- wm8994_update_class_w(codec);
+ wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
+ wm_hubs_update_class_w(codec);
wm8994_handle_pdata(wm8994);
@@ -4075,7 +4016,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(wm8994_dac_widgets));
break;
}
-
wm_hubs_add_analogue_routes(codec, 0, 0);
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
@@ -4140,7 +4080,7 @@ err_irq:
return ret;
}
-static int wm8994_codec_remove(struct snd_soc_codec *codec)
+static int wm8994_codec_remove(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
@@ -4181,14 +4121,10 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
- if (wm8994->mbc)
- release_firmware(wm8994->mbc);
- if (wm8994->mbc_vss)
- release_firmware(wm8994->mbc_vss);
- if (wm8994->enh_eq)
- release_firmware(wm8994->enh_eq);
+ release_firmware(wm8994->mbc);
+ release_firmware(wm8994->mbc_vss);
+ release_firmware(wm8994->enh_eq);
kfree(wm8994->retune_mobile_texts);
-
return 0;
}
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index c724112998d..d77e06f0a67 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <linux/firmware.h>
#include <linux/completion.h>
+#include <linux/workqueue.h>
#include "wm_hubs.h"
@@ -79,6 +80,7 @@ struct wm8994_priv {
struct wm8994_fll_config fll[2], fll_suspend[2];
struct completion fll_locked[2];
bool fll_locked_irq;
+ bool fll_byp;
int vmid_refcount;
int active_refcount;
@@ -126,6 +128,7 @@ struct wm8994_priv {
struct mutex accdet_lock;
struct wm8994_micdet micdet[2];
+ struct delayed_work mic_work;
bool mic_detecting;
bool jack_mic;
int btn_mask;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 1fd63549404..8af422e38fd 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -1770,7 +1770,13 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
+ break;
case SND_SOC_BIAS_PREPARE:
+ /* Put the MICBIASes into regulating mode */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_MODE, 0);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_MODE, 0);
break;
case SND_SOC_BIAS_STANDBY:
@@ -1793,6 +1799,12 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
regcache_cache_only(codec->control_data, false);
regcache_sync(codec->control_data);
}
+
+ /* Bypass the MICBIASes for lowest power */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_MODE, WM8996_MICB1_MODE);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_MODE, WM8996_MICB2_MODE);
break;
case SND_SOC_BIAS_OFF:
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 076c126ed9b..9328270df16 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -774,7 +774,7 @@ static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1"),
SND_SOC_DAPM_INPUT("IN2"),
-SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM9081_POWER_MANAGEMENT, 0, 0),
+SND_SOC_DAPM_DAC("DAC", NULL, WM9081_POWER_MANAGEMENT, 0, 0),
SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
mixer, ARRAY_SIZE(mixer)),
@@ -799,6 +799,7 @@ SND_SOC_DAPM_SUPPLY("TSENSE", WM9081_POWER_MANAGEMENT, 7, 0, NULL, 0),
static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
{ "DAC", NULL, "CLK_SYS" },
{ "DAC", NULL, "CLK_DSP" },
+ { "DAC", NULL, "AIF" },
{ "Mixer", "IN1 Switch", "IN1" },
{ "Mixer", "IN2 Switch", "IN2" },
@@ -1252,7 +1253,7 @@ static const struct snd_soc_dai_ops wm9081_dai_ops = {
static struct snd_soc_dai_driver wm9081_dai = {
.name = "wm9081-hifi",
.playback = {
- .stream_name = "HiFi Playback",
+ .stream_name = "AIF",
.channels_min = 1,
.channels_max = 2,
.rates = WM9081_RATES,
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index cacc6a86b46..e8e782a0c78 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -236,9 +236,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
int reg;
u16 vra;
@@ -250,7 +248,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
else
reg = AC97_PCM_LR_ADC_RATE;
- return ac97_write(codec, reg, runtime->rate);
+ return ac97_write(codec, reg, substream->runtime->rate);
}
#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index b342ae50bcd..a1541414d90 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -467,11 +467,10 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec =rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
int reg;
u16 vra;
+ struct snd_pcm_runtime *runtime = substream->runtime;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
@@ -487,10 +486,9 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_codec *codec = dai->codec;
u16 vra, xsle;
+ struct snd_pcm_runtime *runtime = substream->runtime;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 6c028c47060..dfe957a47f2 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -109,12 +109,103 @@ irqreturn_t wm_hubs_dcs_done(int irq, void *data)
}
EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
+static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
+{
+ int reg;
+
+ /* If we're going via the mixer we'll need to do additional checks */
+ reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1);
+ if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
+ if (reg & ~WM8993_DACL_TO_MIXOUTL) {
+ dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ reg & ~WM8993_DACL_TO_HPOUT1L);
+ return false;
+ } else {
+ dev_vdbg(codec->dev, "HPL connected to mixer\n");
+ }
+ } else {
+ dev_vdbg(codec->dev, "HPL connected to DAC\n");
+ }
+
+ reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2);
+ if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
+ if (reg & ~WM8993_DACR_TO_MIXOUTR) {
+ dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ reg & ~WM8993_DACR_TO_HPOUT1R);
+ return false;
+ } else {
+ dev_vdbg(codec->dev, "HPR connected to mixer\n");
+ }
+ } else {
+ dev_vdbg(codec->dev, "HPR connected to DAC\n");
+ }
+
+ return true;
+}
+
+struct wm_hubs_dcs_cache {
+ struct list_head list;
+ unsigned int left;
+ unsigned int right;
+ u16 dcs_cfg;
+};
+
+static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
+ struct wm_hubs_dcs_cache **entry)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
+ unsigned int left, right;
+
+ left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ left &= WM8993_HPOUT1L_VOL_MASK;
+
+ right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ right &= WM8993_HPOUT1R_VOL_MASK;
+
+ list_for_each_entry(cache, &hubs->dcs_cache, list) {
+ if (cache->left != left || cache->right != right)
+ continue;
+
+ *entry = cache;
+ return true;
+ }
+
+ return false;
+}
+
+static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
+
+ if (hubs->no_cache_dac_hp_direct)
+ return;
+
+ cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
+ if (!cache) {
+ dev_err(codec->dev, "Failed to allocate DCS cache entry\n");
+ return;
+ }
+
+ cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ cache->left &= WM8993_HPOUT1L_VOL_MASK;
+
+ cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ cache->right &= WM8993_HPOUT1R_VOL_MASK;
+
+ cache->dcs_cfg = dcs_cfg;
+
+ list_add_tail(&cache->list, &hubs->dcs_cache);
+}
+
/*
* Startup calibration of the DC servo
*/
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
s8 offset;
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
@@ -129,10 +220,11 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* If we're using a digital only path and have a previously
* callibrated DC servo offset stored then use that. */
- if (hubs->class_w && hubs->class_w_dcs) {
- dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
- hubs->class_w_dcs);
- snd_soc_write(codec, dcs_reg, hubs->class_w_dcs);
+ if (wm_hubs_dac_hp_direct(codec) &&
+ wm_hubs_dcs_cache_get(codec, &cache)) {
+ dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
+ cache->dcs_cfg, cache->left, cache->right);
+ snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
wait_for_dc_servo(codec,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
@@ -207,8 +299,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* Save the callibrated offset if we're in class W mode and
* therefore don't have any analogue signal mixed in. */
- if (hubs->class_w && !hubs->no_cache_class_w)
- hubs->class_w_dcs = dcs_cfg;
+ if (wm_hubs_dac_hp_direct(codec))
+ wm_hubs_dcs_cache_set(codec, dcs_cfg);
}
/*
@@ -223,9 +315,6 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
ret = snd_soc_put_volsw(kcontrol, ucontrol);
- /* Updating the analogue gains invalidates the DC servo cache */
- hubs->class_w_dcs = 0;
-
/* If we're applying an offset correction then updating the
* callibration would be likely to introduce further offsets. */
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
@@ -530,6 +619,86 @@ static int lineout_event(struct snd_soc_dapm_widget *w,
return 0;
}
+void wm_hubs_update_class_w(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
+
+ if (!wm_hubs_dac_hp_direct(codec))
+ enable = false;
+
+ if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
+ enable = false;
+
+ dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
+
+ snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+ WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+}
+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) }
+
+static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ int ret;
+
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ wm_hubs_update_class_w(codec);
+
+ return ret;
+}
+
+#define WM_HUBS_ENUM_W(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = class_w_put_double, \
+ .private_value = (unsigned long)&xenum }
+
+static int class_w_put_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
+ int ret;
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ wm_hubs_update_class_w(codec);
+
+ return ret;
+}
+
+static const char *hp_mux_text[] = {
+ "Mixer",
+ "DAC",
+};
+
+static const struct soc_enum hpl_enum =
+ SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpl_mux =
+ WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
+
+static const struct soc_enum hpr_enum =
+ SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpr_mux =
+ WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
+
static const struct snd_kcontrol_new in1l_pga[] = {
SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
@@ -561,25 +730,25 @@ SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
};
static const struct snd_kcontrol_new left_output_mixer[] = {
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
-SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
};
static const struct snd_kcontrol_new right_output_mixer[] = {
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
-SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
};
static const struct snd_kcontrol_new earpiece_mixer[] = {
@@ -943,6 +1112,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ INIT_LIST_HEAD(&hubs->dcs_cache);
init_completion(&hubs->dcs_done);
snd_soc_dapm_add_routes(dapm, analogue_routes,
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 5705276f494..da2dc899ce6 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -16,6 +16,8 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <sound/control.h>
struct snd_soc_codec;
@@ -30,9 +32,9 @@ struct wm_hubs_data {
int series_startup;
int no_series_update;
- bool no_cache_class_w;
- bool class_w;
- u16 class_w_dcs;
+ bool no_cache_dac_hp_direct;
+ struct list_head dcs_cache;
+ bool (*check_class_w_digital)(struct snd_soc_codec *);
bool lineout1_se;
bool lineout1n_ena;
@@ -58,5 +60,9 @@ extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level);
+extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
+
+extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
+extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
#endif
diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c
index 0678637abd6..bdffab33e16 100644
--- a/sound/soc/ep93xx/ep93xx-ac97.c
+++ b/sound/soc/ep93xx/ep93xx-ac97.c
@@ -87,17 +87,13 @@
* struct ep93xx_ac97_info - EP93xx AC97 controller info structure
* @lock: mutex serializing access to the bus (slot 1 & 2 ops)
* @dev: pointer to the platform device dev structure
- * @mem: physical memory resource for the registers
* @regs: mapped AC97 controller registers
- * @irq: AC97 interrupt number
* @done: bus ops wait here for an interrupt
*/
struct ep93xx_ac97_info {
struct mutex lock;
struct device *dev;
- struct resource *mem;
void __iomem *regs;
- int irq;
struct completion done;
};
@@ -359,66 +355,50 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = {
static int __devinit ep93xx_ac97_probe(struct platform_device *pdev)
{
struct ep93xx_ac97_info *info;
+ struct resource *res;
+ unsigned int irq;
int ret;
- info = kzalloc(sizeof(struct ep93xx_ac97_info), GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- dev_set_drvdata(&pdev->dev, info);
-
- mutex_init(&info->lock);
- init_completion(&info->done);
- info->dev = &pdev->dev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
- info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!info->mem) {
- ret = -ENXIO;
- goto fail_free_info;
- }
+ info->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->regs)
+ return -ENXIO;
- info->irq = platform_get_irq(pdev, 0);
- if (!info->irq) {
- ret = -ENXIO;
- goto fail_free_info;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (!irq)
+ return -ENODEV;
- if (!request_mem_region(info->mem->start, resource_size(info->mem),
- pdev->name)) {
- ret = -EBUSY;
- goto fail_free_info;
- }
+ ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
+ IRQF_TRIGGER_HIGH, pdev->name, info);
+ if (ret)
+ goto fail;
- info->regs = ioremap(info->mem->start, resource_size(info->mem));
- if (!info->regs) {
- ret = -ENOMEM;
- goto fail_release_mem;
- }
+ dev_set_drvdata(&pdev->dev, info);
- ret = request_irq(info->irq, ep93xx_ac97_interrupt, IRQF_TRIGGER_HIGH,
- pdev->name, info);
- if (ret)
- goto fail_unmap_mem;
+ mutex_init(&info->lock);
+ init_completion(&info->done);
+ info->dev = &pdev->dev;
ep93xx_ac97_info = info;
platform_set_drvdata(pdev, info);
ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
if (ret)
- goto fail_free_irq;
+ goto fail;
return 0;
-fail_free_irq:
+fail:
platform_set_drvdata(pdev, NULL);
- free_irq(info->irq, info);
-fail_unmap_mem:
- iounmap(info->regs);
-fail_release_mem:
- release_mem_region(info->mem->start, resource_size(info->mem));
-fail_free_info:
- kfree(info);
-
+ ep93xx_ac97_info = NULL;
+ dev_set_drvdata(&pdev->dev, NULL);
return ret;
}
@@ -431,11 +411,9 @@ static int __devexit ep93xx_ac97_remove(struct platform_device *pdev)
/* disable the AC97 controller */
ep93xx_ac97_write_reg(info, AC97GCR, 0);
- free_irq(info->irq, info);
- iounmap(info->regs);
- release_mem_region(info->mem->start, resource_size(info->mem));
platform_set_drvdata(pdev, NULL);
- kfree(info);
+ ep93xx_ac97_info = NULL;
+ dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
index f7a62348e3f..8df8f6dc474 100644
--- a/sound/soc/ep93xx/ep93xx-i2s.c
+++ b/sound/soc/ep93xx/ep93xx-i2s.c
@@ -63,7 +63,6 @@ struct ep93xx_i2s_info {
struct clk *sclk;
struct clk *lrclk;
struct ep93xx_pcm_dma_params *dma_params;
- struct resource *mem;
void __iomem *regs;
};
@@ -373,38 +372,22 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
struct resource *res;
int err;
- info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
- if (!info) {
- err = -ENOMEM;
- goto fail;
- }
-
- dev_set_drvdata(&pdev->dev, info);
- info->dma_params = ep93xx_i2s_dma_params;
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto fail_free_info;
- }
+ if (!res)
+ return -ENODEV;
- info->mem = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (!info->mem) {
- err = -EBUSY;
- goto fail_free_info;
- }
-
- info->regs = ioremap(info->mem->start, resource_size(info->mem));
- if (!info->regs) {
- err = -ENXIO;
- goto fail_release_mem;
- }
+ info->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->regs)
+ return -ENXIO;
info->mclk = clk_get(&pdev->dev, "mclk");
if (IS_ERR(info->mclk)) {
err = PTR_ERR(info->mclk);
- goto fail_unmap_mem;
+ goto fail;
}
info->sclk = clk_get(&pdev->dev, "sclk");
@@ -419,6 +402,9 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
goto fail_put_sclk;
}
+ dev_set_drvdata(&pdev->dev, info);
+ info->dma_params = ep93xx_i2s_dma_params;
+
err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
if (err)
goto fail_put_lrclk;
@@ -426,17 +412,12 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
return 0;
fail_put_lrclk:
+ dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk);
fail_put_sclk:
clk_put(info->sclk);
fail_put_mclk:
clk_put(info->mclk);
-fail_unmap_mem:
- iounmap(info->regs);
-fail_release_mem:
- release_mem_region(info->mem->start, resource_size(info->mem));
-fail_free_info:
- kfree(info);
fail:
return err;
}
@@ -446,12 +427,10 @@ static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
- iounmap(info->regs);
- release_mem_region(info->mem->start, resource_size(info->mem));
- kfree(info);
return 0;
}
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index d754d34d68a..d70133086ac 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,18 +1,31 @@
-config SND_MPC52xx_DMA
+config SND_SOC_FSL_SSI
tristate
-# ASoC platform support for the Freescale PowerPC SOCs that have an SSI and
-# an Elo DMA controller, such as the MPC8610 and P1022. You will still need to
-# select a platform driver and a codec driver.
-config SND_SOC_POWERPC_SSI
+config SND_SOC_FSL_UTILS
tristate
+
+menuconfig SND_POWERPC_SOC
+ tristate "SoC Audio for Freescale PowerPC CPUs"
depends on FSL_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the PowerPC CPUs.
+
+if SND_POWERPC_SOC
+
+config SND_MPC52xx_DMA
+ tristate
+
+config SND_SOC_POWERPC_DMA
+ tristate
config SND_SOC_MPC8610_HPCD
tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
# I2C is necessary for the CS4270 driver
depends on MPC8610_HPCD && I2C
- select SND_SOC_POWERPC_SSI
+ select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_UTILS
+ select SND_SOC_POWERPC_DMA
select SND_SOC_CS4270
select SND_SOC_CS4270_VD33_ERRATA
default y if MPC8610_HPCD
@@ -23,7 +36,9 @@ config SND_SOC_P1022_DS
tristate "ALSA SoC support for the Freescale P1022 DS board"
# I2C is necessary for the WM8776 driver
depends on P1022_DS && I2C
- select SND_SOC_POWERPC_SSI
+ select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_UTILS
+ select SND_SOC_POWERPC_DMA
select SND_SOC_WM8776
default y if P1022_DS
help
@@ -65,3 +80,103 @@ config SND_MPC52xx_SOC_EFIKA
help
Say Y if you want to add support for sound on the Efika.
+endif # SND_POWERPC_SOC
+
+menuconfig SND_IMX_SOC
+ tristate "SoC Audio for Freescale i.MX CPUs"
+ depends on ARCH_MXC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the i.MX CPUs.
+
+if SND_IMX_SOC
+
+config SND_SOC_IMX_SSI
+ tristate
+
+config SND_SOC_IMX_PCM
+ tristate
+
+config SND_SOC_IMX_PCM_FIQ
+ tristate
+ select FIQ
+ select SND_SOC_IMX_PCM
+
+config SND_SOC_IMX_PCM_DMA
+ tristate
+ select SND_SOC_DMAENGINE_PCM
+ select SND_SOC_IMX_PCM
+
+config SND_SOC_IMX_AUDMUX
+ tristate
+
+config SND_MXC_SOC_WM1133_EV1
+ tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
+ depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
+ select SND_SOC_WM8350
+ select SND_SOC_IMX_PCM_FIQ
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_IMX_SSI
+ help
+ Enable support for audio on the i.MX31ADS with the WM1133-EV1
+ PMIC board with WM8835x fitted.
+
+config SND_SOC_MX27VIS_AIC32X4
+ tristate "SoC audio support for Visstrim M10 boards"
+ depends on MACH_IMX27_VISSTRIM_M10 && I2C
+ select SND_SOC_TLV320AIC32X4
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_IMX_SSI
+ help
+ Say Y if you want to add support for SoC audio on Visstrim SM10
+ board with TLV320AIC32X4 codec.
+
+config SND_SOC_PHYCORE_AC97
+ tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
+ depends on MACH_PCM043 || MACH_PCA100
+ select SND_SOC_AC97_BUS
+ select SND_SOC_WM9712
+ select SND_SOC_IMX_PCM_FIQ
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_IMX_SSI
+ help
+ Say Y if you want to add support for SoC audio on Phytec phyCORE
+ and phyCARD boards in AC97 mode
+
+config SND_SOC_EUKREA_TLV320
+ tristate "Eukrea TLV320"
+ depends on MACH_EUKREA_MBIMX27_BASEBOARD \
+ || MACH_EUKREA_MBIMXSD25_BASEBOARD \
+ || MACH_EUKREA_MBIMXSD35_BASEBOARD \
+ || MACH_EUKREA_MBIMXSD51_BASEBOARD
+ depends on I2C
+ select SND_SOC_TLV320AIC23
+ select SND_SOC_IMX_PCM_FIQ
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_IMX_SSI
+ help
+ Enable I2S based access to the TLV320AIC23B codec attached
+ to the SSI interface
+
+config SND_SOC_IMX_SGTL5000
+ tristate "SoC Audio support for i.MX boards with sgtl5000"
+ depends on OF && I2C
+ select SND_SOC_SGTL5000
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_UTILS
+ help
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a sgtl5000 codec.
+
+config SND_SOC_IMX_MC13783
+ tristate "SoC Audio support for I.MX boards with mc13783"
+ depends on MFD_MC13783
+ select SND_SOC_IMX_SSI
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_MC13783
+ select SND_SOC_IMX_PCM_DMA
+
+endif # SND_IMX_SOC
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b4a38c0ac58..5f3cf3f52ea 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -8,8 +8,11 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
# Freescale PowerPC SSI/DMA Platform Support
snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
-obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
+obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
+obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
@@ -20,3 +23,29 @@ obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
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
+
+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
+snd-soc-imx-pcm-y := imx-pcm.o
+snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
+snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
+
+# i.MX Machine Support
+snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
+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-mc13783-objs := imx-mc13783.o
+
+obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
+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_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 7d4475cfdb2..efb9ede0120 100644
--- a/sound/soc/imx/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -7,7 +7,7 @@
* which is Copyright 2009 Simtec Electronics
* and on sound/soc/imx/phycore-ac97.c which is
* Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 2eb407fa3b4..4ed2afd4778 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -11,11 +11,15 @@
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <sound/core.h>
@@ -25,6 +29,26 @@
#include <sound/soc.h>
#include "fsl_ssi.h"
+#include "imx-pcm.h"
+
+#ifdef PPC
+#define read_ssi(addr) in_be32(addr)
+#define write_ssi(val, addr) out_be32(addr, val)
+#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
+#elif defined ARM
+#define read_ssi(addr) readl(addr)
+#define write_ssi(val, addr) writel(val, addr)
+/*
+ * FIXME: Proper locking should be added at write_ssi_mask caller level
+ * to ensure this register read/modify/write sequence is race free.
+ */
+static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
+{
+ u32 val = readl(addr);
+ val = (val & ~clear) | set;
+ writel(val, addr);
+}
+#endif
/**
* FSLSSI_I2S_RATES: sample rates supported by the I2S
@@ -94,6 +118,13 @@ struct fsl_ssi_private {
struct device_attribute dev_attr;
struct platform_device *pdev;
+ bool new_binding;
+ bool ssi_on_imx;
+ struct clk *clk;
+ struct platform_device *imx_pcm_pdev;
+ struct imx_pcm_dma_params dma_params_tx;
+ struct imx_pcm_dma_params dma_params_rx;
+
struct {
unsigned int rfrc;
unsigned int tfrc;
@@ -145,7 +176,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
- sisr = in_be32(&ssi->sisr) & SIER_FLAGS;
+ sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
if (sisr & CCSR_SSI_SISR_RFRC) {
ssi_private->stats.rfrc++;
@@ -260,7 +291,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
/* Clear the bits that we set */
if (sisr2)
- out_be32(&ssi->sisr, sisr2);
+ write_ssi(sisr2, &ssi->sisr);
return ret;
}
@@ -295,7 +326,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* SSI needs to be disabled before updating the registers we set
* here.
*/
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
/*
* Program the SSI into I2S Slave Non-Network Synchronous mode.
@@ -303,20 +334,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
*
* FIXME: Little-endian samples require a different shift dir
*/
- clrsetbits_be32(&ssi->scr,
+ write_ssi_mask(&ssi->scr,
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
| (synchronous ? CCSR_SSI_SCR_SYN : 0));
- out_be32(&ssi->stcr,
- CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+ write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
- CCSR_SSI_STCR_TSCKP);
+ CCSR_SSI_STCR_TSCKP, &ssi->stcr);
- out_be32(&ssi->srcr,
- CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+ write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
- CCSR_SSI_SRCR_RSCKP);
+ CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
/*
* The DC and PM bits are only used if the SSI is the clock
@@ -324,7 +353,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
*/
/* Enable the interrupts and DMA requests */
- out_be32(&ssi->sier, SIER_FLAGS);
+ write_ssi(SIER_FLAGS, &ssi->sier);
/*
* Set the watermark for transmit FIFI 0 and receive FIFO 0. We
@@ -339,9 +368,9 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* make this value larger (and maybe we should), but this way
* data will be written to memory as soon as it's available.
*/
- out_be32(&ssi->sfcsr,
- CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
- CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2));
+ write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
+ CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
+ &ssi->sfcsr);
/*
* We keep the SSI disabled because if we enable it, then the
@@ -393,6 +422,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
ssi_private->second_stream = substream;
}
+ if (ssi_private->ssi_on_imx)
+ snd_soc_dai_set_dma_data(dai, substream,
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ &ssi_private->dma_params_tx :
+ &ssi_private->dma_params_rx);
+
return 0;
}
@@ -417,7 +452,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
unsigned int sample_size =
snd_pcm_format_width(params_format(hw_params));
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
- int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
+ int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
/*
* If we're in synchronous mode, and the SSI is already enabled,
@@ -439,9 +474,9 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
/* In synchronous mode, the SSI uses STCCR for capture */
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
ssi_private->cpu_dai_drv.symmetric_rates)
- clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+ write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
else
- clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+ write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
return 0;
}
@@ -466,19 +501,19 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- setbits32(&ssi->scr,
+ write_ssi_mask(&ssi->scr, 0,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
else
- setbits32(&ssi->scr,
+ write_ssi_mask(&ssi->scr, 0,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
else
- clrbits32(&ssi->scr, CCSR_SSI_SCR_RE);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
break;
default:
@@ -510,7 +545,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
if (!ssi_private->first_stream) {
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
}
}
@@ -622,12 +657,6 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
if (!of_device_is_available(np))
return -ENODEV;
- /* Check for a codec-handle property. */
- if (!of_get_property(np, "codec-handle", NULL)) {
- dev_err(&pdev->dev, "missing codec-handle property\n");
- return -ENODEV;
- }
-
/* We only support the SSI in "I2S Slave" mode */
sprop = of_get_property(np, "fsl,mode", NULL);
if (!sprop || strcmp(sprop, "i2s-slave")) {
@@ -692,6 +721,50 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;
+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
+ u32 dma_events[2];
+ ssi_private->ssi_on_imx = true;
+
+ ssi_private->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ssi_private->clk)) {
+ ret = PTR_ERR(ssi_private->clk);
+ dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+ goto error_irq;
+ }
+ clk_prepare_enable(ssi_private->clk);
+
+ /*
+ * We have burstsize be "fifo_depth - 2" to match the SSI
+ * watermark setting in fsl_ssi_startup().
+ */
+ ssi_private->dma_params_tx.burstsize =
+ ssi_private->fifo_depth - 2;
+ ssi_private->dma_params_rx.burstsize =
+ ssi_private->fifo_depth - 2;
+ ssi_private->dma_params_tx.dma_addr =
+ ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
+ ssi_private->dma_params_rx.dma_addr =
+ ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
+ /*
+ * TODO: This is a temporary solution and should be changed
+ * to use generic DMA binding later when the helplers get in.
+ */
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "fsl,ssi-dma-events", dma_events, 2);
+ if (ret) {
+ dev_err(&pdev->dev, "could not get dma events\n");
+ goto error_clk;
+ }
+ ssi_private->dma_params_tx.dma = dma_events[0];
+ ssi_private->dma_params_rx.dma = dma_events[1];
+
+ ssi_private->dma_params_tx.shared_peripheral =
+ of_device_is_compatible(of_get_parent(np),
+ "fsl,spba-bus");
+ ssi_private->dma_params_rx.shared_peripheral =
+ ssi_private->dma_params_tx.shared_peripheral;
+ }
+
/* Initialize the the device_attribute structure */
dev_attr = &ssi_private->dev_attr;
sysfs_attr_init(&dev_attr->attr);
@@ -715,6 +788,26 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
goto error_dev;
}
+ 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);
+ goto error_dev;
+ }
+ }
+
+ /*
+ * If codec-handle property is missing from SSI node, we assume
+ * that the machine driver uses new binding which does not require
+ * SSI driver to trigger machine driver's probe.
+ */
+ if (!of_get_property(np, "codec-handle", NULL)) {
+ ssi_private->new_binding = true;
+ goto done;
+ }
+
/* Trigger the machine driver's probe function. The platform driver
* name of the machine driver is taken from /compatible property of the
* device tree. We also pass the address of the CPU DAI driver
@@ -736,15 +829,24 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
goto error_dai;
}
+done:
return 0;
error_dai:
+ if (ssi_private->ssi_on_imx)
+ platform_device_unregister(ssi_private->imx_pcm_pdev);
snd_soc_unregister_dai(&pdev->dev);
error_dev:
dev_set_drvdata(&pdev->dev, NULL);
device_remove_file(&pdev->dev, dev_attr);
+error_clk:
+ if (ssi_private->ssi_on_imx) {
+ clk_disable_unprepare(ssi_private->clk);
+ clk_put(ssi_private->clk);
+ }
+
error_irq:
free_irq(ssi_private->irq, ssi_private);
@@ -764,7 +866,13 @@ static int fsl_ssi_remove(struct platform_device *pdev)
{
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
- platform_device_unregister(ssi_private->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);
+ clk_disable_unprepare(ssi_private->clk);
+ clk_put(ssi_private->clk);
+ }
snd_soc_unregister_dai(&pdev->dev);
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
@@ -779,6 +887,7 @@ static int fsl_ssi_remove(struct platform_device *pdev)
static const struct of_device_id fsl_ssi_ids[] = {
{ .compatible = "fsl,mpc8610-ssi", },
+ { .compatible = "fsl,imx21-ssi", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
new file mode 100644
index 00000000000..b9e42b503a3
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.c
@@ -0,0 +1,91 @@
+/**
+ * Freescale ALSA SoC Machine driver utility
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <sound/soc.h>
+
+#include "fsl_utils.h"
+
+/**
+ * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node
+ *
+ * @ssi_np: pointer to the SSI device tree node
+ * @name: name of the phandle pointing to the dma channel
+ * @dai: ASoC DAI link pointer to be filled with platform_name
+ * @dma_channel_id: dma channel id to be returned
+ * @dma_id: dma id to be returned
+ *
+ * This function determines the dma and channel id for given SSI node. It
+ * also discovers the platform_name for the ASoC DAI link.
+ */
+int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
+ const char *name,
+ struct snd_soc_dai_link *dai,
+ unsigned int *dma_channel_id,
+ unsigned int *dma_id)
+{
+ struct resource res;
+ struct device_node *dma_channel_np, *dma_np;
+ const u32 *iprop;
+ int ret;
+
+ dma_channel_np = of_parse_phandle(ssi_np, name, 0);
+ if (!dma_channel_np)
+ return -EINVAL;
+
+ if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) {
+ of_node_put(dma_channel_np);
+ return -EINVAL;
+ }
+
+ /* Determine the dev_name for the device_node. This code mimics the
+ * behavior of of_device_make_bus_id(). We need this because ASoC uses
+ * the dev_name() of the device to match the platform (DMA) device with
+ * the CPU (SSI) device. It's all ugly and hackish, but it works (for
+ * now).
+ *
+ * dai->platform name should already point to an allocated buffer.
+ */
+ ret = of_address_to_resource(dma_channel_np, 0, &res);
+ if (ret) {
+ of_node_put(dma_channel_np);
+ return ret;
+ }
+ snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
+ (unsigned long long) res.start, dma_channel_np->name);
+
+ iprop = of_get_property(dma_channel_np, "cell-index", NULL);
+ if (!iprop) {
+ of_node_put(dma_channel_np);
+ return -EINVAL;
+ }
+ *dma_channel_id = be32_to_cpup(iprop);
+
+ dma_np = of_get_parent(dma_channel_np);
+ iprop = of_get_property(dma_np, "cell-index", NULL);
+ if (!iprop) {
+ of_node_put(dma_np);
+ return -EINVAL;
+ }
+ *dma_id = be32_to_cpup(iprop);
+
+ of_node_put(dma_np);
+ of_node_put(dma_channel_np);
+
+ return 0;
+}
+EXPORT_SYMBOL(fsl_asoc_get_dma_channel);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale ASoC utility code");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h
new file mode 100644
index 00000000000..b2951126527
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.h
@@ -0,0 +1,26 @@
+/**
+ * Freescale ALSA SoC Machine driver utility
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_UTILS_H
+#define _FSL_UTILS_H
+
+#define DAI_NAME_SIZE 32
+
+struct snd_soc_dai_link;
+struct device_node;
+
+int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,
+ struct snd_soc_dai_link *dai,
+ unsigned int *dma_channel_id,
+ unsigned int *dma_id);
+
+#endif /* _FSL_UTILS_H */
diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index f23700359c6..f23700359c6 100644
--- a/sound/soc/imx/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
diff --git a/sound/soc/imx/imx-audmux.h b/sound/soc/fsl/imx-audmux.h
index 04ebbab8d7b..04ebbab8d7b 100644
--- a/sound/soc/imx/imx-audmux.h
+++ b/sound/soc/fsl/imx-audmux.h
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
new file mode 100644
index 00000000000..f59c3494366
--- /dev/null
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -0,0 +1,156 @@
+/*
+ * imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec
+ *
+ * Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
+ *
+ * Heavly based on phycore-mc13783:
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/mc13783.h"
+#include "imx-ssi.h"
+#include "imx-audmux.h"
+
+#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
+ SND_SOC_DAIFMT_CBM_CFM)
+
+static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc,
+ 4, 16);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0, 0xfffffffc, 2, 16);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops imx_mc13783_hifi_ops = {
+ .hw_params = imx_mc13783_hifi_hw_params,
+};
+
+static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
+ {
+ .name = "MC13783",
+ .stream_name = "Sound",
+ .codec_dai_name = "mc13783-hifi",
+ .codec_name = "mc13783-codec",
+ .cpu_dai_name = "imx-ssi.0",
+ .platform_name = "imx-pcm-audio.0",
+ .ops = &imx_mc13783_hifi_ops,
+ .symmetric_rates = 1,
+ .dai_fmt = FMT_SSI,
+ },
+};
+
+static const struct snd_soc_dapm_widget imx_mc13783_widget[] = {
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
+ {"Speaker", NULL, "LSP"},
+ {"Headphone", NULL, "HSL"},
+ {"Headphone", NULL, "HSR"},
+
+ {"MC1LIN", NULL, "MC1 Bias"},
+ {"MC2IN", NULL, "MC2 Bias"},
+ {"MC1 Bias", NULL, "Mic"},
+ {"MC2 Bias", NULL, "Mic"},
+};
+
+static struct snd_soc_card imx_mc13783 = {
+ .name = "imx_mc13783",
+ .dai_link = imx_mc13783_dai_mc13783,
+ .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783),
+ .dapm_widgets = imx_mc13783_widget,
+ .num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget),
+ .dapm_routes = imx_mc13783_routes,
+ .num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes),
+};
+
+static int __devinit imx_mc13783_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ imx_mc13783.dev = &pdev->dev;
+
+ ret = snd_soc_register_card(&imx_mc13783);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
+ IMX_AUDMUX_V2_PTCR_SYN,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
+ IMX_AUDMUX_V2_PDCR_MODE(1) |
+ IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
+ imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
+ IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
+ IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
+
+ return ret;
+}
+
+static int __devexit imx_mc13783_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&imx_mc13783);
+
+ return 0;
+}
+
+static struct platform_driver imx_mc13783_audio_driver = {
+ .driver = {
+ .name = "imx_mc13783",
+ .owner = THIS_MODULE,
+ },
+ .probe = imx_mc13783_probe,
+ .remove = __devexit_p(imx_mc13783_remove)
+};
+
+module_platform_driver(imx_mc13783_audio_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch");
+MODULE_DESCRIPTION("imx with mc13783 codec ALSA SoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx_mc13783");
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/fsl/imx-pcm-dma.c
index 6b818de2fc0..f3c0a5ef35c 100644
--- a/sound/soc/imx/imx-pcm-dma-mx2.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -109,7 +109,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
- dma_data->peripheral_type = IMX_DMATYPE_SSI;
+ dma_data->peripheral_type = dma_params->shared_peripheral ?
+ IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI;
dma_data->priority = DMA_PRIO_HIGH;
dma_data->dma_request = dma_params->dma;
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 456b7d723d6..456b7d723d6 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
index 93dc360b177..93dc360b177 100644
--- a/sound/soc/imx/imx-pcm.c
+++ b/sound/soc/fsl/imx-pcm.c
diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index b5f5c3acf34..83c0ed7d55c 100644
--- a/sound/soc/imx/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -22,6 +22,7 @@ struct imx_pcm_dma_params {
int dma;
unsigned long dma_addr;
int burstsize;
+ bool shared_peripheral; /* The peripheral is on SPBA bus */
};
int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
new file mode 100644
index 00000000000..3a729caeb8c
--- /dev/null
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -0,0 +1,221 @@
+/*
+ * 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.h>
+#include <linux/of_platform.h>
+#include <linux/of_i2c.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+#include "../codecs/sgtl5000.h"
+#include "imx-audmux.h"
+
+#define DAI_NAME_SIZE 32
+
+struct imx_sgtl5000_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;
+};
+
+static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct imx_sgtl5000_data *data = container_of(rtd->card,
+ struct imx_sgtl5000_data, card);
+ struct device *dev = rtd->card->dev;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK,
+ data->clk_frequency, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "could not set codec driver clock params\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static int __devinit imx_sgtl5000_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 i2c_client *codec_dev;
+ struct imx_sgtl5000_data *data;
+ int int_port, ext_port;
+ int ret;
+
+ 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_PTCR_TCSEL(int_port),
+ 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) {
+ 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 = 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->dai.name = "HiFi";
+ data->dai.stream_name = "HiFi";
+ data->dai.codec_dai_name = "sgtl5000";
+ data->dai.codec_of_node = codec_np;
+ data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
+ data->dai.platform_name = "imx-pcm-audio";
+ 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;
+
+ 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_sgtl5000_dapm_widgets;
+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets);
+
+ 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);
+clk_fail:
+ clk_put(data->codec_clk);
+fail:
+ if (ssi_np)
+ of_node_put(ssi_np);
+ if (codec_np)
+ of_node_put(codec_np);
+
+ return ret;
+}
+
+static int __devexit 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;
+}
+
+static const struct of_device_id imx_sgtl5000_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-sgtl5000", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids);
+
+static struct platform_driver imx_sgtl5000_driver = {
+ .driver = {
+ .name = "imx-sgtl5000",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_sgtl5000_dt_ids,
+ },
+ .probe = imx_sgtl5000_probe,
+ .remove = __devexit_p(imx_sgtl5000_remove),
+};
+module_platform_driver(imx_sgtl5000_driver);
+
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-sgtl5000");
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 4f81ed45632..cf3ed0362c9 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -28,7 +28,7 @@
* value. When we read the same register two times (and the register still
* contains the same value) these status bits are not set. We work
* around this by not polling these bits but only wait a fixed delay.
- *
+ *
*/
#include <linux/clk.h>
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index 5744e86ca87..5744e86ca87 100644
--- a/sound/soc/imx/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 3fea5a15ffe..60bcba1bc30 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -14,18 +14,16 @@
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/slab.h>
-#include <linux/of_i2c.h>
#include <sound/soc.h>
#include <asm/fsl_guts.h>
#include "fsl_dma.h"
#include "fsl_ssi.h"
+#include "fsl_utils.h"
/* There's only one global utilities register */
static phys_addr_t guts_phys;
-#define DAI_NAME_SIZE 32
-
/**
* mpc8610_hpcd_data: machine-specific ASoC device data
*
@@ -43,7 +41,6 @@ struct mpc8610_hpcd_data {
unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
char codec_dai_name[DAI_NAME_SIZE];
- char codec_name[DAI_NAME_SIZE];
char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
};
@@ -181,141 +178,6 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
};
/**
- * get_node_by_phandle_name - get a node by its phandle name
- *
- * This function takes a node, the name of a property in that node, and a
- * compatible string. Assuming the property is a phandle to another node,
- * it returns that node, (optionally) if that node is compatible.
- *
- * If the property is not a phandle, or the node it points to is not compatible
- * with the specific string, then NULL is returned.
- */
-static struct device_node *get_node_by_phandle_name(struct device_node *np,
- const char *name,
- const char *compatible)
-{
- const phandle *ph;
- int len;
-
- ph = of_get_property(np, name, &len);
- if (!ph || (len != sizeof(phandle)))
- return NULL;
-
- np = of_find_node_by_phandle(*ph);
- if (!np)
- return NULL;
-
- if (compatible && !of_device_is_compatible(np, compatible)) {
- of_node_put(np);
- return NULL;
- }
-
- return np;
-}
-
-/**
- * get_parent_cell_index -- return the cell-index of the parent of a node
- *
- * Return the value of the cell-index property of the parent of the given
- * node. This is used for DMA channel nodes that need to know the DMA ID
- * of the controller they are on.
- */
-static int get_parent_cell_index(struct device_node *np)
-{
- struct device_node *parent = of_get_parent(np);
- const u32 *iprop;
-
- if (!parent)
- return -1;
-
- iprop = of_get_property(parent, "cell-index", NULL);
- of_node_put(parent);
-
- if (!iprop)
- return -1;
-
- return be32_to_cpup(iprop);
-}
-
-/**
- * codec_node_dev_name - determine the dev_name for a codec node
- *
- * This function determines the dev_name for an I2C node. This is the name
- * that would be returned by dev_name() if this device_node were part of a
- * 'struct device' It's ugly and hackish, but it works.
- *
- * The dev_name for such devices include the bus number and I2C address. For
- * example, "cs4270.0-004f".
- */
-static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
-{
- const u32 *iprop;
- int addr;
- char temp[DAI_NAME_SIZE];
- struct i2c_client *i2c;
-
- of_modalias_node(np, temp, DAI_NAME_SIZE);
-
- iprop = of_get_property(np, "reg", NULL);
- if (!iprop)
- return -EINVAL;
-
- addr = be32_to_cpup(iprop);
-
- /* We need the adapter number */
- i2c = of_find_i2c_device_by_node(np);
- if (!i2c)
- return -ENODEV;
-
- snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
-
- return 0;
-}
-
-static int get_dma_channel(struct device_node *ssi_np,
- const char *name,
- struct snd_soc_dai_link *dai,
- unsigned int *dma_channel_id,
- unsigned int *dma_id)
-{
- struct resource res;
- struct device_node *dma_channel_np;
- const u32 *iprop;
- int ret;
-
- dma_channel_np = get_node_by_phandle_name(ssi_np, name,
- "fsl,ssi-dma-channel");
- if (!dma_channel_np)
- return -EINVAL;
-
- /* Determine the dev_name for the device_node. This code mimics the
- * behavior of of_device_make_bus_id(). We need this because ASoC uses
- * the dev_name() of the device to match the platform (DMA) device with
- * the CPU (SSI) device. It's all ugly and hackish, but it works (for
- * now).
- *
- * dai->platform name should already point to an allocated buffer.
- */
- ret = of_address_to_resource(dma_channel_np, 0, &res);
- if (ret)
- return ret;
- snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
- (unsigned long long) res.start, dma_channel_np->name);
-
- iprop = of_get_property(dma_channel_np, "cell-index", NULL);
- if (!iprop) {
- of_node_put(dma_channel_np);
- return -EINVAL;
- }
-
- *dma_channel_id = be32_to_cpup(iprop);
- *dma_id = get_parent_cell_index(dma_channel_np);
- of_node_put(dma_channel_np);
-
- return 0;
-}
-
-/**
* mpc8610_hpcd_probe: platform probe function for the machine driver
*
* Although this is a machine driver, the SSI node is the "master" node with
@@ -352,16 +214,8 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
machine_data->dai[0].ops = &mpc8610_hpcd_ops;
- /* Determine the codec name, it will be used as the codec DAI name */
- ret = codec_node_dev_name(codec_np, machine_data->codec_name,
- DAI_NAME_SIZE);
- if (ret) {
- dev_err(&pdev->dev, "invalid codec node %s\n",
- codec_np->full_name);
- ret = -EINVAL;
- goto error;
- }
- machine_data->dai[0].codec_name = machine_data->codec_name;
+ /* ASoC core can match codec with device node */
+ machine_data->dai[0].codec_of_node = codec_np;
/* The DAI name from the codec (snd_soc_dai_driver.name) */
machine_data->dai[0].codec_dai_name = "cs4270-hifi";
@@ -458,9 +312,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
/* Find the playback DMA channel to use. */
machine_data->dai[0].platform_name = machine_data->platform_name[0];
- ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0],
- &machine_data->dma_channel_id[0],
- &machine_data->dma_id[0]);
+ ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
+ &machine_data->dai[0],
+ &machine_data->dma_channel_id[0],
+ &machine_data->dma_id[0]);
if (ret) {
dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
goto error;
@@ -468,9 +323,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
/* Find the capture DMA channel to use. */
machine_data->dai[1].platform_name = machine_data->platform_name[1];
- ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1],
- &machine_data->dma_channel_id[1],
- &machine_data->dma_id[1]);
+ ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
+ &machine_data->dai[1],
+ &machine_data->dma_channel_id[1],
+ &machine_data->dma_id[1]);
if (ret) {
dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
goto error;
diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
index f6d04ad4bb3..f6d04ad4bb3 100644
--- a/sound/soc/imx/mx27vis-aic32x4.c
+++ b/sound/soc/fsl/mx27vis-aic32x4.c
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 982a1c94498..50adf4032bc 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -14,12 +14,12 @@
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/slab.h>
-#include <linux/of_i2c.h>
#include <sound/soc.h>
#include <asm/fsl_guts.h>
#include "fsl_dma.h"
#include "fsl_ssi.h"
+#include "fsl_utils.h"
/* P1022-specific PMUXCR and DMUXCR bit definitions */
@@ -57,8 +57,6 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
/* There's only one global utilities register */
static phys_addr_t guts_phys;
-#define DAI_NAME_SIZE 32
-
/**
* machine_data: machine-specific ASoC device data
*
@@ -75,7 +73,6 @@ struct machine_data {
unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
- char codec_name[DAI_NAME_SIZE];
char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
};
@@ -191,136 +188,6 @@ static struct snd_soc_ops p1022_ds_ops = {
};
/**
- * get_node_by_phandle_name - get a node by its phandle name
- *
- * This function takes a node, the name of a property in that node, and a
- * compatible string. Assuming the property is a phandle to another node,
- * it returns that node, (optionally) if that node is compatible.
- *
- * If the property is not a phandle, or the node it points to is not compatible
- * with the specific string, then NULL is returned.
- */
-static struct device_node *get_node_by_phandle_name(struct device_node *np,
- const char *name, const char *compatible)
-{
- np = of_parse_phandle(np, name, 0);
- if (!np)
- return NULL;
-
- if (!of_device_is_compatible(np, compatible)) {
- of_node_put(np);
- return NULL;
- }
-
- return np;
-}
-
-/**
- * get_parent_cell_index -- return the cell-index of the parent of a node
- *
- * Return the value of the cell-index property of the parent of the given
- * node. This is used for DMA channel nodes that need to know the DMA ID
- * of the controller they are on.
- */
-static int get_parent_cell_index(struct device_node *np)
-{
- struct device_node *parent = of_get_parent(np);
- const u32 *iprop;
- int ret = -1;
-
- if (!parent)
- return -1;
-
- iprop = of_get_property(parent, "cell-index", NULL);
- if (iprop)
- ret = be32_to_cpup(iprop);
-
- of_node_put(parent);
-
- return ret;
-}
-
-/**
- * codec_node_dev_name - determine the dev_name for a codec node
- *
- * This function determines the dev_name for an I2C node. This is the name
- * that would be returned by dev_name() if this device_node were part of a
- * 'struct device' It's ugly and hackish, but it works.
- *
- * The dev_name for such devices include the bus number and I2C address. For
- * example, "cs4270-codec.0-004f".
- */
-static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
-{
- const u32 *iprop;
- int addr;
- char temp[DAI_NAME_SIZE];
- struct i2c_client *i2c;
-
- of_modalias_node(np, temp, DAI_NAME_SIZE);
-
- iprop = of_get_property(np, "reg", NULL);
- if (!iprop)
- return -EINVAL;
-
- addr = be32_to_cpup(iprop);
-
- /* We need the adapter number */
- i2c = of_find_i2c_device_by_node(np);
- if (!i2c)
- return -ENODEV;
-
- snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
-
- return 0;
-}
-
-static int get_dma_channel(struct device_node *ssi_np,
- const char *name,
- struct snd_soc_dai_link *dai,
- unsigned int *dma_channel_id,
- unsigned int *dma_id)
-{
- struct resource res;
- struct device_node *dma_channel_np;
- const u32 *iprop;
- int ret;
-
- dma_channel_np = get_node_by_phandle_name(ssi_np, name,
- "fsl,ssi-dma-channel");
- if (!dma_channel_np)
- return -EINVAL;
-
- /* Determine the dev_name for the device_node. This code mimics the
- * behavior of of_device_make_bus_id(). We need this because ASoC uses
- * the dev_name() of the device to match the platform (DMA) device with
- * the CPU (SSI) device. It's all ugly and hackish, but it works (for
- * now).
- *
- * dai->platform name should already point to an allocated buffer.
- */
- ret = of_address_to_resource(dma_channel_np, 0, &res);
- if (ret) {
- of_node_put(dma_channel_np);
- return ret;
- }
- snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
- (unsigned long long) res.start, dma_channel_np->name);
-
- iprop = of_get_property(dma_channel_np, "cell-index", NULL);
- if (!iprop) {
- of_node_put(dma_channel_np);
- return -EINVAL;
- }
-
- *dma_channel_id = be32_to_cpup(iprop);
- *dma_id = get_parent_cell_index(dma_channel_np);
- of_node_put(dma_channel_np);
-
- return 0;
-}
-
-/**
* p1022_ds_probe: platform probe function for the machine driver
*
* Although this is a machine driver, the SSI node is the "master" node with
@@ -357,15 +224,8 @@ static int p1022_ds_probe(struct platform_device *pdev)
mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
mdata->dai[0].ops = &p1022_ds_ops;
- /* Determine the codec name, it will be used as the codec DAI name */
- ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE);
- if (ret) {
- dev_err(&pdev->dev, "invalid codec node %s\n",
- codec_np->full_name);
- ret = -EINVAL;
- goto error;
- }
- mdata->dai[0].codec_name = mdata->codec_name;
+ /* ASoC core can match codec with device node */
+ mdata->dai[0].codec_of_node = codec_np;
/* We register two DAIs per SSI, one for playback and the other for
* capture. We support codecs that have separate DAIs for both playback
@@ -462,9 +322,9 @@ static int p1022_ds_probe(struct platform_device *pdev)
/* Find the playback DMA channel to use. */
mdata->dai[0].platform_name = mdata->platform_name[0];
- ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
- &mdata->dma_channel_id[0],
- &mdata->dma_id[0]);
+ ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
+ &mdata->dma_channel_id[0],
+ &mdata->dma_id[0]);
if (ret) {
dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
goto error;
@@ -472,9 +332,9 @@ static int p1022_ds_probe(struct platform_device *pdev)
/* Find the capture DMA channel to use. */
mdata->dai[1].platform_name = mdata->platform_name[1];
- ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
- &mdata->dma_channel_id[1],
- &mdata->dma_id[1]);
+ ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
+ &mdata->dma_channel_id[1],
+ &mdata->dma_id[1]);
if (ret) {
dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
goto error;
diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
index f8da6dd115e..f8da6dd115e 100644
--- a/sound/soc/imx/phycore-ac97.c
+++ b/sound/soc/fsl/phycore-ac97.c
diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
index fe54a69073e..fe54a69073e 100644
--- a/sound/soc/imx/wm1133-ev1.c
+++ b/sound/soc/fsl/wm1133-ev1.c
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
new file mode 100644
index 00000000000..610f6125164
--- /dev/null
+++ b/sound/soc/generic/Kconfig
@@ -0,0 +1,4 @@
+config SND_SIMPLE_CARD
+ tristate "ASoC Simple sound card support"
+ help
+ This option enables generic simple sound card support
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
new file mode 100644
index 00000000000..9c3b246792b
--- /dev/null
+++ b/sound/soc/generic/Makefile
@@ -0,0 +1,3 @@
+snd-soc-simple-card-objs := simple-card.o
+
+obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
new file mode 100644
index 00000000000..b4b4cab3023
--- /dev/null
+++ b/sound/soc/generic/simple-card.c
@@ -0,0 +1,114 @@
+/*
+ * ASoC simple sound card support
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/simple_card.h>
+
+#define asoc_simple_get_card_info(p) \
+ container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
+
+static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd);
+ struct asoc_simple_dai_init_info *iinfo = cinfo->init;
+ struct snd_soc_dai *codec = rtd->codec_dai;
+ struct snd_soc_dai *cpu = rtd->cpu_dai;
+ unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt;
+ unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt;
+ int ret;
+
+ if (codec_daifmt) {
+ ret = snd_soc_dai_set_fmt(codec, codec_daifmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (iinfo->sysclk) {
+ ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_daifmt) {
+ ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int asoc_simple_card_probe(struct platform_device *pdev)
+{
+ struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+
+ if (!cinfo) {
+ dev_err(&pdev->dev, "no info for asoc-simple-card\n");
+ return -EINVAL;
+ }
+
+ if (!cinfo->name ||
+ !cinfo->card ||
+ !cinfo->cpu_dai ||
+ !cinfo->codec ||
+ !cinfo->platform ||
+ !cinfo->codec_dai) {
+ dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n");
+ return -EINVAL;
+ }
+
+ /*
+ * init snd_soc_dai_link
+ */
+ cinfo->snd_link.name = cinfo->name;
+ cinfo->snd_link.stream_name = cinfo->name;
+ cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai;
+ cinfo->snd_link.platform_name = cinfo->platform;
+ cinfo->snd_link.codec_name = cinfo->codec;
+ cinfo->snd_link.codec_dai_name = cinfo->codec_dai;
+
+ /* enable snd_link.init if cinfo has settings */
+ if (cinfo->init)
+ cinfo->snd_link.init = asoc_simple_card_dai_init;
+
+ /*
+ * init snd_soc_card
+ */
+ cinfo->snd_card.name = cinfo->card;
+ cinfo->snd_card.owner = THIS_MODULE;
+ cinfo->snd_card.dai_link = &cinfo->snd_link;
+ cinfo->snd_card.num_links = 1;
+ cinfo->snd_card.dev = &pdev->dev;
+
+ return snd_soc_register_card(&cinfo->snd_card);
+}
+
+static int asoc_simple_card_remove(struct platform_device *pdev)
+{
+ struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+
+ return snd_soc_unregister_card(&cinfo->snd_card);
+}
+
+static struct platform_driver asoc_simple_card = {
+ .driver = {
+ .name = "asoc-simple-card",
+ },
+ .probe = asoc_simple_card_probe,
+ .remove = asoc_simple_card_remove,
+};
+
+module_platform_driver(asoc_simple_card);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ASoC Simple Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
deleted file mode 100644
index d83e5d0b5d5..00000000000
--- a/sound/soc/imx/Kconfig
+++ /dev/null
@@ -1,79 +0,0 @@
-menuconfig SND_IMX_SOC
- tristate "SoC Audio for Freescale i.MX CPUs"
- depends on ARCH_MXC
- help
- Say Y or M if you want to add support for codecs attached to
- the i.MX SSI interface.
-
-
-if SND_IMX_SOC
-
-config SND_SOC_IMX_SSI
- tristate
-
-config SND_SOC_IMX_PCM
- tristate
-
-config SND_MXC_SOC_FIQ
- tristate
- select FIQ
- select SND_SOC_IMX_PCM
-
-config SND_MXC_SOC_MX2
- select SND_SOC_DMAENGINE_PCM
- tristate
- select SND_SOC_IMX_PCM
-
-config SND_SOC_IMX_AUDMUX
- tristate
-
-config SND_MXC_SOC_WM1133_EV1
- tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
- depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
- select SND_SOC_WM8350
- select SND_MXC_SOC_FIQ
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Enable support for audio on the i.MX31ADS with the WM1133-EV1
- PMIC board with WM8835x fitted.
-
-config SND_SOC_MX27VIS_AIC32X4
- tristate "SoC audio support for Visstrim M10 boards"
- depends on MACH_IMX27_VISSTRIM_M10 && I2C
- select SND_SOC_TLV320AIC32X4
- select SND_MXC_SOC_MX2
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Say Y if you want to add support for SoC audio on Visstrim SM10
- board with TLV320AIC32X4 codec.
-
-config SND_SOC_PHYCORE_AC97
- tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
- depends on MACH_PCM043 || MACH_PCA100
- select SND_SOC_AC97_BUS
- select SND_SOC_WM9712
- select SND_MXC_SOC_FIQ
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Say Y if you want to add support for SoC audio on Phytec phyCORE
- and phyCARD boards in AC97 mode
-
-config SND_SOC_EUKREA_TLV320
- tristate "Eukrea TLV320"
- depends on MACH_EUKREA_MBIMX27_BASEBOARD \
- || MACH_EUKREA_MBIMXSD25_BASEBOARD \
- || MACH_EUKREA_MBIMXSD35_BASEBOARD \
- || MACH_EUKREA_MBIMXSD51_BASEBOARD
- depends on I2C
- select SND_SOC_TLV320AIC23
- select SND_MXC_SOC_FIQ
- select SND_SOC_IMX_AUDMUX
- select SND_SOC_IMX_SSI
- help
- Enable I2S based access to the TLV320AIC23B codec attached
- to the SSI interface
-
-endif # SND_IMX_SOC
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
deleted file mode 100644
index f5db3e92d0d..00000000000
--- a/sound/soc/imx/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-# i.MX Platform Support
-snd-soc-imx-ssi-objs := imx-ssi.o
-snd-soc-imx-audmux-objs := imx-audmux.o
-
-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
-snd-soc-imx-pcm-y := imx-pcm.o
-snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o
-snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o
-
-# i.MX Machine Support
-snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
-snd-soc-phycore-ac97-objs := phycore-ac97.o
-snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
-snd-soc-wm1133-ev1-objs := wm1133-ev1.o
-
-obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
-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
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index a5af7c42e62..41349670ada 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -346,7 +346,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
/* Playback */
dma_config = &i2s->pcm_config_playback.dma_config;
- dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
@@ -355,7 +355,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
/* Capture */
dma_config = &i2s->pcm_config_capture.dma_config;
- dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
dma_config->flags = JZ4740_DMA_DST_AUTOINC;
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index e373fbbc97a..373dec90579 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -220,28 +220,16 @@ static struct snd_soc_platform_driver mxs_soc_platform = {
.pcm_free = mxs_pcm_free,
};
-static int __devinit mxs_soc_platform_probe(struct platform_device *pdev)
+int __devinit mxs_pcm_platform_register(struct device *dev)
{
- return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform);
+ return snd_soc_register_platform(dev, &mxs_soc_platform);
}
+EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
-static int __devexit mxs_soc_platform_remove(struct platform_device *pdev)
+void __devexit mxs_pcm_platform_unregister(struct device *dev)
{
- snd_soc_unregister_platform(&pdev->dev);
-
- return 0;
+ snd_soc_unregister_platform(dev);
}
-
-static struct platform_driver mxs_pcm_driver = {
- .driver = {
- .name = "mxs-pcm-audio",
- .owner = THIS_MODULE,
- },
- .probe = mxs_soc_platform_probe,
- .remove = __devexit_p(mxs_soc_platform_remove),
-};
-
-module_platform_driver(mxs_pcm_driver);
+EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:mxs-pcm-audio");
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
index 5f01a9124b3..35ba2ca4238 100644
--- a/sound/soc/mxs/mxs-pcm.h
+++ b/sound/soc/mxs/mxs-pcm.h
@@ -24,4 +24,7 @@ struct mxs_pcm_dma_params {
int chan_num;
};
+int mxs_pcm_platform_register(struct device *dev);
+void mxs_pcm_platform_unregister(struct device *dev);
+
#endif
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 7fd224bb732..aba71bfa33b 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -18,6 +18,8 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
@@ -621,37 +623,57 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int mxs_saif_probe(struct platform_device *pdev)
+static int __devinit mxs_saif_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct resource *iores, *dmares;
struct mxs_saif *saif;
struct mxs_saif_platform_data *pdata;
struct pinctrl *pinctrl;
int ret = 0;
- if (pdev->id >= ARRAY_SIZE(mxs_saif))
+
+ if (!np && pdev->id >= ARRAY_SIZE(mxs_saif))
return -EINVAL;
saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL);
if (!saif)
return -ENOMEM;
- mxs_saif[pdev->id] = saif;
- saif->id = pdev->id;
-
- pdata = pdev->dev.platform_data;
- if (pdata && !pdata->master_mode) {
- saif->master_id = pdata->master_id;
- if (saif->master_id < 0 ||
- saif->master_id >= ARRAY_SIZE(mxs_saif) ||
- saif->master_id == saif->id) {
- dev_err(&pdev->dev, "get wrong master id\n");
- return -EINVAL;
+ if (np) {
+ struct device_node *master;
+ saif->id = of_alias_get_id(np, "saif");
+ if (saif->id < 0)
+ return saif->id;
+ /*
+ * If there is no "fsl,saif-master" phandle, it's a saif
+ * master. Otherwise, it's a slave and its phandle points
+ * to the master.
+ */
+ master = of_parse_phandle(np, "fsl,saif-master", 0);
+ if (!master) {
+ saif->master_id = saif->id;
+ } else {
+ saif->master_id = of_alias_get_id(master, "saif");
+ if (saif->master_id < 0)
+ return saif->master_id;
}
} else {
- saif->master_id = saif->id;
+ saif->id = pdev->id;
+ pdata = pdev->dev.platform_data;
+ if (pdata && !pdata->master_mode)
+ saif->master_id = pdata->master_id;
+ else
+ saif->master_id = saif->id;
+ }
+
+ if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) {
+ dev_err(&pdev->dev, "get wrong master id\n");
+ return -EINVAL;
}
+ mxs_saif[saif->id] = saif;
+
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
@@ -677,12 +699,19 @@ static int mxs_saif_probe(struct platform_device *pdev)
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmares) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "failed to get dma resource: %d\n",
- ret);
- goto failed_get_resource;
+ /*
+ * 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");
+ goto failed_get_resource;
+ }
+ } else {
+ saif->dma_param.chan_num = dmares->start;
}
- saif->dma_param.chan_num = dmares->start;
saif->irq = platform_get_irq(pdev, 0);
if (saif->irq < 0) {
@@ -716,24 +745,14 @@ static int mxs_saif_probe(struct platform_device *pdev)
goto failed_get_resource;
}
- saif->soc_platform_pdev = platform_device_alloc(
- "mxs-pcm-audio", pdev->id);
- if (!saif->soc_platform_pdev) {
- ret = -ENOMEM;
- goto failed_pdev_alloc;
- }
-
- platform_set_drvdata(saif->soc_platform_pdev, saif);
- ret = platform_device_add(saif->soc_platform_pdev);
+ ret = mxs_pcm_platform_register(&pdev->dev);
if (ret) {
- dev_err(&pdev->dev, "failed to add soc platform device\n");
- goto failed_pdev_add;
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto failed_pdev_alloc;
}
return 0;
-failed_pdev_add:
- platform_device_put(saif->soc_platform_pdev);
failed_pdev_alloc:
snd_soc_unregister_dai(&pdev->dev);
failed_get_resource:
@@ -746,13 +765,19 @@ static int __devexit mxs_saif_remove(struct platform_device *pdev)
{
struct mxs_saif *saif = platform_get_drvdata(pdev);
- platform_device_unregister(saif->soc_platform_pdev);
+ mxs_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev);
clk_put(saif->clk);
return 0;
}
+static const struct of_device_id mxs_saif_dt_ids[] = {
+ { .compatible = "fsl,imx28-saif", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_saif_dt_ids);
+
static struct platform_driver mxs_saif_driver = {
.probe = mxs_saif_probe,
.remove = __devexit_p(mxs_saif_remove),
@@ -760,6 +785,7 @@ static struct platform_driver mxs_saif_driver = {
.driver = {
.name = "mxs-saif",
.owner = THIS_MODULE,
+ .of_match_table = mxs_saif_dt_ids,
},
};
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h
index 12c91e4eb94..3cb342e5bc9 100644
--- a/sound/soc/mxs/mxs-saif.h
+++ b/sound/soc/mxs/mxs-saif.h
@@ -123,7 +123,6 @@ struct mxs_saif {
unsigned int cur_rate;
unsigned int ongoing;
- struct platform_device *soc_platform_pdev;
u32 fifo_underrun;
u32 fifo_overrun;
};
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 60f052b7cf2..3e6e8764b2e 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -18,6 +18,8 @@
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -90,7 +92,7 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
.codec_dai_name = "sgtl5000",
.codec_name = "sgtl5000.0-000a",
.cpu_dai_name = "mxs-saif.0",
- .platform_name = "mxs-pcm-audio.0",
+ .platform_name = "mxs-saif.0",
.ops = &mxs_sgtl5000_hifi_ops,
}, {
.name = "HiFi Rx",
@@ -98,7 +100,7 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
.codec_dai_name = "sgtl5000",
.codec_name = "sgtl5000.0-000a",
.cpu_dai_name = "mxs-saif.1",
- .platform_name = "mxs-pcm-audio.1",
+ .platform_name = "mxs-saif.1",
.ops = &mxs_sgtl5000_hifi_ops,
},
};
@@ -110,11 +112,48 @@ static struct snd_soc_card mxs_sgtl5000 = {
.num_links = ARRAY_SIZE(mxs_sgtl5000_dai),
};
+static int __devinit 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;
+
+ if (!np)
+ return 1; /* no device tree */
+
+ saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
+ saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (!saif_np[0] || !saif_np[1] || !codec_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ mxs_sgtl5000_dai[i].codec_name = NULL;
+ mxs_sgtl5000_dai[i].codec_of_node = codec_np;
+ mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
+ mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i];
+ mxs_sgtl5000_dai[i].platform_name = NULL;
+ mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
+ }
+
+ of_node_put(codec_np);
+ of_node_put(saif_np[0]);
+ of_node_put(saif_np[1]);
+
+ return ret;
+}
+
static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mxs_sgtl5000;
int ret;
+ ret = mxs_sgtl5000_probe_dt(pdev);
+ if (ret < 0)
+ return ret;
+
/*
* Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
* The Sgtl5000 sysclk is derived from saif0 mclk and it's range
@@ -148,10 +187,17 @@ static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
+ { .compatible = "fsl,mxs-audio-sgtl5000", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
+
static struct platform_driver mxs_sgtl5000_audio_driver = {
.driver = {
.name = "mxs-sgtl5000",
.owner = THIS_MODULE,
+ .of_match_table = mxs_sgtl5000_dt_ids,
},
.probe = mxs_sgtl5000_probe,
.remove = __devexit_p(mxs_sgtl5000_remove),
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index deafbfaacdb..9ccfa5e1c11 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -113,6 +113,7 @@ config SND_OMAP_SOC_OMAP4_HDMI
tristate "SoC Audio support for Texas Instruments OMAP4 HDMI"
depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4
select SND_OMAP_SOC_HDMI
+ select SND_SOC_OMAP_HDMI_CODEC
help
Say Y if you want to add support for SoC HDMI audio on Texas Instruments
OMAP4 chips
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index fd04ce13903..1c2aa7fab3f 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -85,14 +85,12 @@ struct pxa2xx_pcm_dma_data {
char name[20];
};
-static struct pxa2xx_pcm_dma_params *
-pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
+static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
+ int out, struct pxa2xx_pcm_dma_params *dma_data)
{
struct pxa2xx_pcm_dma_data *dma;
- dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
- if (dma == NULL)
- return NULL;
+ dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params);
snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
width4 ? "32-bit" : "16-bit", out ? "out" : "in");
@@ -103,8 +101,6 @@ pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
(DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
(width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
dma->params.dev_addr = ssp->phys_base + SSDR;
-
- return &dma->params;
}
static int pxa_ssp_startup(struct snd_pcm_substream *substream,
@@ -112,6 +108,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
{
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
struct ssp_device *ssp = priv->ssp;
+ struct pxa2xx_pcm_dma_data *dma;
int ret = 0;
if (!cpu_dai->active) {
@@ -119,8 +116,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
pxa_ssp_disable(ssp);
}
- kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
- snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+ snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params);
return ret;
}
@@ -573,18 +572,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
- /* generate correct DMA params */
- kfree(dma_data);
-
/* Network mode with one active slot (ttsa == 1) can be used
* to force 16-bit frame width on the wire (for S16_LE), even
* with two channels. Use 16-bit DMA transfers for this case.
*/
- dma_data = pxa_ssp_get_dma_params(ssp,
- ((chn == 2) && (ttsa != 1)) || (width == 32),
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+ pxa_ssp_set_dma_params(ssp,
+ ((chn == 2) && (ttsa != 1)) || (width == 32),
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data);
/* we can only change the settings if the port is not in use */
if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index d08583790d2..3075a426124 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -166,7 +166,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct pxa2xx_pcm_dma_params *dma_data;
BUG_ON(IS_ERR(clk_i2s));
- clk_enable(clk_i2s);
+ clk_prepare_enable(clk_i2s);
clk_ena = 1;
pxa_i2s_wait();
@@ -259,7 +259,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
SACR0 &= ~SACR0_ENB;
pxa_i2s_wait();
if (clk_ena) {
- clk_disable(clk_i2s);
+ clk_disable_unprepare(clk_i2s);
clk_ena = 0;
}
}
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index e7416851bf7..c82c646b8a0 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -23,10 +23,10 @@ static int littlemill_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 snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
int ret;
- if (dapm->dev != codec_dai->dev)
+ if (dapm->dev != aif1_dai->dev)
return 0;
switch (level) {
@@ -36,7 +36,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
* then do so now, otherwise these are noops.
*/
if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
- ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
WM8994_FLL_SRC_MCLK2, 32768,
sample_rate * 512);
if (ret < 0) {
@@ -44,7 +44,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
return ret;
}
- ret = snd_soc_dai_set_sysclk(codec_dai,
+ ret = snd_soc_dai_set_sysclk(aif1_dai,
WM8994_SYSCLK_FLL1,
sample_rate * 512,
SND_SOC_CLOCK_IN);
@@ -66,25 +66,25 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
int ret;
- if (dapm->dev != codec_dai->dev)
+ if (dapm->dev != aif1_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_STANDBY:
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0) {
- pr_err("Failed to switch away from FLL: %d\n", ret);
+ pr_err("Failed to switch away from FLL1: %d\n", ret);
return ret;
}
- ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+ ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
0, 0, 0);
if (ret < 0) {
- pr_err("Failed to stop FLL: %d\n", ret);
+ pr_err("Failed to stop FLL1: %d\n", ret);
return ret;
}
break;
@@ -131,6 +131,14 @@ static struct snd_soc_ops littlemill_ops = {
.hw_params = littlemill_hw_params,
};
+static const struct snd_soc_pcm_stream baseband_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
static struct snd_soc_dai_link littlemill_dai[] = {
{
.name = "CPU",
@@ -143,13 +151,75 @@ static struct snd_soc_dai_link littlemill_dai[] = {
| SND_SOC_DAIFMT_CBM_CFM,
.ops = &littlemill_ops,
},
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm8994-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &baseband_params,
+ },
};
+static int bbclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
+ WM8994_FLL_SRC_BCLK, 64 * 8000,
+ 8000 * 256);
+ if (ret < 0) {
+ pr_err("Failed to start FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2,
+ 8000 * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err("Failed to switch away from FLL2: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
+ 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL2: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
SND_SOC_DAPM_MIC("DMIC", NULL),
+
+ SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0,
+ bbclk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static struct snd_soc_dapm_route audio_paths[] = {
@@ -162,6 +232,8 @@ static struct snd_soc_dapm_route audio_paths[] = {
{ "DMIC", NULL, "MICBIAS2" }, /* Default for DMICBIAS jumper */
{ "DMIC1DAT", NULL, "DMIC" },
{ "DMIC2DAT", NULL, "DMIC" },
+
+ { "AIF2CLK", NULL, "Baseband Clock" },
};
static struct snd_soc_jack littlemill_headset;
@@ -169,10 +241,16 @@ static struct snd_soc_jack littlemill_headset;
static int littlemill_late_probe(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = card->rtd[0].codec;
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
int ret;
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+ ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 4adff934f77..6abf341c4a2 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -21,33 +21,6 @@
#define MCLK1_RATE (44100 * 512)
#define CLKOUT_RATE (44100 * 256)
-static int lowland_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops lowland_ops = {
- .hw_params = lowland_hw_params,
-};
-
static struct snd_soc_jack lowland_headset;
/* Headset jack detection DAPM pins */
@@ -101,6 +74,25 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+
+ snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT");
+
+ /* At any time the WM9081 is active it will have this clock */
+ return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
+ CLKOUT_RATE, 0);
+}
+
+static const struct snd_soc_pcm_stream sub_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
static struct snd_soc_dai_link lowland_dai[] = {
{
.name = "CPU",
@@ -109,7 +101,8 @@ static struct snd_soc_dai_link lowland_dai[] = {
.codec_dai_name = "wm5100-aif1",
.platform_name = "samsung-audio",
.codec_name = "wm5100.1-001a",
- .ops = &lowland_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.init = lowland_wm5100_init,
},
{
@@ -118,24 +111,20 @@ static struct snd_soc_dai_link lowland_dai[] = {
.cpu_dai_name = "wm5100-aif2",
.codec_dai_name = "wm1250-ev1",
.codec_name = "wm1250-ev1.1-0027",
- .ops = &lowland_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ignore_suspend = 1,
},
-};
-
-static int lowland_wm9081_init(struct snd_soc_dapm_context *dapm)
-{
- snd_soc_dapm_nc_pin(dapm, "LINEOUT");
-
- /* At any time the WM9081 is active it will have this clock */
- return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0,
- CLKOUT_RATE, 0);
-}
-
-static struct snd_soc_aux_dev lowland_aux_dev[] = {
{
- .name = "wm9081",
+ .name = "Sub Speaker",
+ .stream_name = "Sub Speaker",
+ .cpu_dai_name = "wm5100-aif3",
+ .codec_dai_name = "wm9081-hifi",
.codec_name = "wm9081.1-006c",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &sub_params,
.init = lowland_wm9081_init,
},
};
@@ -180,8 +169,6 @@ static struct snd_soc_card lowland = {
.owner = THIS_MODULE,
.dai_link = lowland_dai,
.num_links = ARRAY_SIZE(lowland_dai),
- .aux_dev = lowland_aux_dev,
- .num_aux_devs = ARRAY_SIZE(lowland_aux_dev),
.codec_conf = lowland_codec_conf,
.num_configs = ARRAY_SIZE(lowland_codec_conf),
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index f9ab7707a3e..a4a9fc7e8c7 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -92,33 +92,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
return 0;
}
-static int speyside_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
- | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops speyside_ops = {
- .hw_params = speyside_hw_params,
-};
-
static struct snd_soc_jack speyside_headset;
/* Headset jack detection DAPM pins */
@@ -208,7 +181,8 @@ static struct snd_soc_dai_link speyside_dai[] = {
.platform_name = "samsung-audio",
.codec_name = "wm8996.1-001a",
.init = speyside_wm8996_init,
- .ops = &speyside_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
},
{
.name = "Baseband",
@@ -216,7 +190,8 @@ static struct snd_soc_dai_link speyside_dai[] = {
.cpu_dai_name = "wm8996-aif2",
.codec_dai_name = "wm1250-ev1",
.codec_name = "wm1250-ev1.1-0027",
- .ops = &speyside_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
.ignore_suspend = 1,
},
};
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index d8e06a607a2..6bcb1164d59 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -22,6 +22,7 @@ config SND_SOC_SH4_SSI
config SND_SOC_SH4_FSI
tristate "SH4 FSI support"
+ select SND_SIMPLE_CARD
help
This option enables FSI sound support
@@ -46,29 +47,6 @@ config SND_SH7760_AC97
This option enables generic sound support for the first
AC97 unit of the SH7760.
-config SND_FSI_AK4642
- tristate "FSI-AK4642 sound support"
- depends on SND_SOC_SH4_FSI && I2C
- select SND_SOC_AK4642
- help
- This option enables generic sound support for the
- FSI - AK4642 unit
-
-config SND_FSI_DA7210
- tristate "FSI-DA7210 sound support"
- depends on SND_SOC_SH4_FSI && I2C
- select SND_SOC_DA7210
- help
- This option enables generic sound support for the
- FSI - DA7210 unit
-
-config SND_FSI_HDMI
- tristate "FSI-HDMI sound support"
- depends on SND_SOC_SH4_FSI && FB_SH_MOBILE_HDMI
- help
- This option enables generic sound support for the
- FSI - HDMI unit
-
config SND_SIU_MIGOR
tristate "SIU sound support on Migo-R"
depends on SH_MIGOR
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index 94476d4c0fd..849b387d17d 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -14,13 +14,7 @@ obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o
## boards
snd-soc-sh7760-ac97-objs := sh7760-ac97.o
-snd-soc-fsi-ak4642-objs := fsi-ak4642.o
-snd-soc-fsi-da7210-objs := fsi-da7210.o
-snd-soc-fsi-hdmi-objs := fsi-hdmi.o
snd-soc-migor-objs := migor.o
obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o
-obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o
-obj-$(CONFIG_SND_FSI_DA7210) += snd-soc-fsi-da7210.o
-obj-$(CONFIG_SND_FSI_HDMI) += snd-soc-fsi-hdmi.o
obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
deleted file mode 100644
index 97f540aabbd..00000000000
--- a/sound/soc/sh/fsi-ak4642.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * FSI-AK464x sound support for ms7724se
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-struct fsi_ak4642_data {
- const char *name;
- const char *card;
- const char *cpu_dai;
- const char *codec;
- const char *platform;
- int id;
-};
-
-static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec = rtd->codec_dai;
- struct snd_soc_dai *cpu = rtd->cpu_dai;
- int ret;
-
- ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J |
- SND_SOC_DAIFMT_CBS_CFS);
-
- return ret;
-}
-
-static struct snd_soc_dai_link fsi_dai_link = {
- .codec_dai_name = "ak4642-hifi",
- .init = fsi_ak4642_dai_init,
-};
-
-static struct snd_soc_card fsi_soc_card = {
- .owner = THIS_MODULE,
- .dai_link = &fsi_dai_link,
- .num_links = 1,
-};
-
-static struct platform_device *fsi_snd_device;
-
-static int fsi_ak4642_probe(struct platform_device *pdev)
-{
- int ret = -ENOMEM;
- struct fsi_ak4642_info *pinfo = pdev->dev.platform_data;
-
- if (!pinfo) {
- dev_err(&pdev->dev, "no info for fsi ak4642\n");
- goto out;
- }
-
- fsi_snd_device = platform_device_alloc("soc-audio", pinfo->id);
- if (!fsi_snd_device)
- goto out;
-
- fsi_dai_link.name = pinfo->name;
- fsi_dai_link.stream_name = pinfo->name;
- fsi_dai_link.cpu_dai_name = pinfo->cpu_dai;
- fsi_dai_link.platform_name = pinfo->platform;
- fsi_dai_link.codec_name = pinfo->codec;
- fsi_soc_card.name = pinfo->card;
-
- platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
- ret = platform_device_add(fsi_snd_device);
-
- if (ret)
- platform_device_put(fsi_snd_device);
-
-out:
- return ret;
-}
-
-static int fsi_ak4642_remove(struct platform_device *pdev)
-{
- platform_device_unregister(fsi_snd_device);
- return 0;
-}
-
-static struct platform_driver fsi_ak4642 = {
- .driver = {
- .name = "fsi-ak4642-audio",
- },
- .probe = fsi_ak4642_probe,
- .remove = fsi_ak4642_remove,
-};
-
-module_platform_driver(fsi_ak4642);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card");
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
deleted file mode 100644
index 1dd3354c741..00000000000
--- a/sound/soc/sh/fsi-da7210.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * fsi-da7210.c
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec = rtd->codec_dai;
- struct snd_soc_dai *cpu = rtd->cpu_dai;
- int ret;
-
- ret = snd_soc_dai_set_fmt(codec,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_CBS_CFS);
-
- return ret;
-}
-
-static struct snd_soc_dai_link fsi_da7210_dai = {
- .name = "DA7210",
- .stream_name = "DA7210",
- .cpu_dai_name = "fsib-dai", /* FSI B */
- .codec_dai_name = "da7210-hifi",
- .platform_name = "sh_fsi.0",
- .codec_name = "da7210-codec.0-001a",
- .init = fsi_da7210_init,
-};
-
-static struct snd_soc_card fsi_soc_card = {
- .name = "FSI-DA7210",
- .owner = THIS_MODULE,
- .dai_link = &fsi_da7210_dai,
- .num_links = 1,
-};
-
-static struct platform_device *fsi_da7210_snd_device;
-
-static int __init fsi_da7210_sound_init(void)
-{
- int ret;
-
- fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
- if (!fsi_da7210_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(fsi_da7210_snd_device, &fsi_soc_card);
- ret = platform_device_add(fsi_da7210_snd_device);
- if (ret)
- platform_device_put(fsi_da7210_snd_device);
-
- return ret;
-}
-
-static void __exit fsi_da7210_sound_exit(void)
-{
- platform_device_unregister(fsi_da7210_snd_device);
-}
-
-module_init(fsi_da7210_sound_init);
-module_exit(fsi_da7210_sound_exit);
-
-/* Module information */
-MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c
deleted file mode 100644
index 6e41908323e..00000000000
--- a/sound/soc/sh/fsi-hdmi.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * FSI - HDMI sound support
- *
- * Copyright (C) 2010 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-struct fsi_hdmi_data {
- const char *cpu_dai;
- const char *card;
- int id;
-};
-
-static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *cpu = rtd->cpu_dai;
- int ret;
-
- ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM);
-
- return ret;
-}
-
-static struct snd_soc_dai_link fsi_dai_link = {
- .name = "HDMI",
- .stream_name = "HDMI",
- .codec_dai_name = "sh_mobile_hdmi-hifi",
- .platform_name = "sh_fsi2",
- .codec_name = "sh-mobile-hdmi",
- .init = fsi_hdmi_dai_init,
-};
-
-static struct snd_soc_card fsi_soc_card = {
- .owner = THIS_MODULE,
- .dai_link = &fsi_dai_link,
- .num_links = 1,
-};
-
-static struct platform_device *fsi_snd_device;
-
-static int fsi_hdmi_probe(struct platform_device *pdev)
-{
- int ret = -ENOMEM;
- const struct platform_device_id *id_entry;
- struct fsi_hdmi_data *pdata;
-
- id_entry = pdev->id_entry;
- if (!id_entry) {
- dev_err(&pdev->dev, "unknown fsi hdmi\n");
- return -ENODEV;
- }
-
- pdata = (struct fsi_hdmi_data *)id_entry->driver_data;
-
- fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
- if (!fsi_snd_device)
- goto out;
-
- fsi_dai_link.cpu_dai_name = pdata->cpu_dai;
- fsi_soc_card.name = pdata->card;
-
- platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
- ret = platform_device_add(fsi_snd_device);
-
- if (ret)
- platform_device_put(fsi_snd_device);
-
-out:
- return ret;
-}
-
-static int fsi_hdmi_remove(struct platform_device *pdev)
-{
- platform_device_unregister(fsi_snd_device);
- return 0;
-}
-
-static struct fsi_hdmi_data fsi2_a_hdmi = {
- .cpu_dai = "fsia-dai",
- .card = "FSI2A-HDMI",
- .id = FSI_PORT_A,
-};
-
-static struct fsi_hdmi_data fsi2_b_hdmi = {
- .cpu_dai = "fsib-dai",
- .card = "FSI2B-HDMI",
- .id = FSI_PORT_B,
-};
-
-static struct platform_device_id fsi_id_table[] = {
- /* FSI 2 */
- { "sh_fsi2_a_hdmi", (kernel_ulong_t)&fsi2_a_hdmi },
- { "sh_fsi2_b_hdmi", (kernel_ulong_t)&fsi2_b_hdmi },
- {},
-};
-
-static struct platform_driver fsi_hdmi = {
- .driver = {
- .name = "fsi-hdmi-audio",
- },
- .probe = fsi_hdmi_probe,
- .remove = fsi_hdmi_remove,
- .id_table = fsi_id_table,
-};
-
-module_platform_driver(fsi_hdmi);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic SH4 FSI-HDMI sound card");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 74ed2dffbff..7cee22515d9 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -132,6 +132,25 @@
typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
/*
+ * bus options
+ *
+ * 0x000000BA
+ *
+ * A : sample widtht 16bit setting
+ * B : sample widtht 24bit setting
+ */
+
+#define SHIFT_16DATA 0
+#define SHIFT_24DATA 4
+
+#define PACKAGE_24BITBUS_BACK 0
+#define PACKAGE_24BITBUS_FRONT 1
+#define PACKAGE_16BITBUS_STREAM 2
+
+#define BUSOP_SET(s, a) ((a) << SHIFT_ ## s ## DATA)
+#define BUSOP_GET(s, a) (((a) >> SHIFT_ ## s ## DATA) & 0xF)
+
+/*
* FSI driver use below type name for variable
*
* xxx_num : number of data
@@ -189,6 +208,11 @@ struct fsi_stream {
int oerr_num;
/*
+ * bus options
+ */
+ u32 bus_option;
+
+ /*
* thse are initialized by fsi_handler_init()
*/
struct fsi_stream_handler *handler;
@@ -211,8 +235,7 @@ struct fsi_priv {
struct fsi_stream playback;
struct fsi_stream capture;
- u32 do_fmt;
- u32 di_fmt;
+ u32 fmt;
int chan_num:16;
int clk_master:1;
@@ -321,6 +344,10 @@ static void _fsi_master_mask_set(struct fsi_master *master,
/*
* basic function
*/
+static int fsi_version(struct fsi_master *master)
+{
+ return master->core->ver;
+}
static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
{
@@ -495,6 +522,7 @@ static void fsi_stream_init(struct fsi_priv *fsi,
io->period_samples = fsi_frame2sample(fsi, runtime->period_size);
io->period_pos = 0;
io->sample_width = samples_to_bytes(runtime, 1);
+ io->bus_option = 0;
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
fsi_stream_handler_call(io, init, fsi, io);
@@ -522,6 +550,7 @@ static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
io->period_samples = 0;
io->period_pos = 0;
io->sample_width = 0;
+ io->bus_option = 0;
io->oerr_num = 0;
io->uerr_num = 0;
spin_unlock_irqrestore(&master->lock, flags);
@@ -581,6 +610,53 @@ static int fsi_stream_remove(struct fsi_priv *fsi)
}
/*
+ * format/bus/dma setting
+ */
+static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io,
+ u32 bus, struct device *dev)
+{
+ struct fsi_master *master = fsi_get_master(fsi);
+ int is_play = fsi_stream_is_play(fsi, io);
+ u32 fmt = fsi->fmt;
+
+ if (fsi_version(master) >= 2) {
+ u32 dma = 0;
+
+ /*
+ * FSI2 needs DMA/Bus setting
+ */
+ switch (bus) {
+ case PACKAGE_24BITBUS_FRONT:
+ fmt |= CR_BWS_24;
+ dma |= VDMD_FRONT;
+ dev_dbg(dev, "24bit bus / package in front\n");
+ break;
+ case PACKAGE_16BITBUS_STREAM:
+ fmt |= CR_BWS_16;
+ dma |= VDMD_STREAM;
+ dev_dbg(dev, "16bit bus / stream mode\n");
+ break;
+ case PACKAGE_24BITBUS_BACK:
+ default:
+ fmt |= CR_BWS_24;
+ dma |= VDMD_BACK;
+ dev_dbg(dev, "24bit bus / package in back\n");
+ break;
+ }
+
+ if (is_play)
+ fsi_reg_write(fsi, OUT_DMAC, dma);
+ else
+ fsi_reg_write(fsi, IN_DMAC, dma);
+ }
+
+ if (is_play)
+ fsi_reg_write(fsi, DO_FMT, fmt);
+ else
+ fsi_reg_write(fsi, DI_FMT, fmt);
+}
+
+/*
* irq function
*/
@@ -629,11 +705,6 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
struct fsi_master *master = fsi_get_master(fsi);
u32 mask, val;
- if (master->core->ver < 2) {
- pr_err("fsi: register access err (%s)\n", __func__);
- return;
- }
-
mask = BP | SE;
val = enable ? mask : 0;
@@ -648,9 +719,7 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
long rate, int enable)
{
- struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(fsi);
- int fsi_ver = master->core->ver;
int ret;
if (!set_rate)
@@ -682,10 +751,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
data |= (0x3 << 12);
break;
case SH_FSI_ACKMD_32:
- if (fsi_ver < 2)
- dev_err(dev, "unsupported ACKMD\n");
- else
- data |= (0x4 << 12);
+ data |= (0x4 << 12);
break;
}
@@ -708,10 +774,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
data |= (0x4 << 8);
break;
case SH_FSI_BPFMD_16:
- if (fsi_ver < 2)
- dev_err(dev, "unsupported ACKMD\n");
- else
- data |= (0x7 << 8);
+ data |= (0x7 << 8);
break;
}
@@ -728,11 +791,26 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
*/
static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples)
{
- u16 *buf = (u16 *)_buf;
+ u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE;
int i;
- for (i = 0; i < samples; i++)
- fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
+ if (enable_stream) {
+ /*
+ * stream mode
+ * see
+ * fsi_pio_push_init()
+ */
+ u32 *buf = (u32 *)_buf;
+
+ for (i = 0; i < samples / 2; i++)
+ fsi_reg_write(fsi, DODT, buf[i]);
+ } else {
+ /* normal mode */
+ u16 *buf = (u16 *)_buf;
+
+ for (i = 0; i < samples; i++)
+ fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
+ }
}
static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples)
@@ -872,12 +950,44 @@ static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
+static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE;
+
+ /*
+ * we can use 16bit stream mode
+ * when "playback" and "16bit data"
+ * and platform allows "stream mode"
+ * see
+ * fsi_pio_push16()
+ */
+ if (enable_stream)
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+ else
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+ return 0;
+}
+
+static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ /*
+ * always 24bit bus, package back when "capture"
+ */
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+ return 0;
+}
+
static struct fsi_stream_handler fsi_pio_push_handler = {
+ .init = fsi_pio_push_init,
.transfer = fsi_pio_push,
.start_stop = fsi_pio_start_stop,
};
static struct fsi_stream_handler fsi_pio_pop_handler = {
+ .init = fsi_pio_pop_init,
.transfer = fsi_pio_pop,
.start_stop = fsi_pio_start_stop,
};
@@ -919,6 +1029,13 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ /*
+ * 24bit data : 24bit bus / package in back
+ * 16bit data : 16bit bus / stream mode
+ */
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+
io->dma = dma_map_single(dai->dev, runtime->dma_area,
snd_pcm_lib_buffer_bytes(io->substream), dir);
return 0;
@@ -1055,25 +1172,9 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
int start)
{
- u32 bws;
- u32 dma;
+ u32 enable = start ? DMA_ON : 0;
- switch (io->sample_width * start) {
- case 2:
- bws = CR_BWS_16;
- dma = VDMD_STREAM | DMA_ON;
- break;
- case 4:
- bws = CR_BWS_24;
- dma = VDMD_BACK | DMA_ON;
- break;
- default:
- bws = 0;
- dma = 0;
- }
-
- fsi_reg_mask_set(fsi, DO_FMT, CR_BWS_MASK, bws);
- fsi_reg_write(fsi, OUT_DMAC, dma);
+ fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable);
}
static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
@@ -1176,8 +1277,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
struct fsi_stream *io,
struct device *dev)
{
- struct fsi_master *master = fsi_get_master(fsi);
- int fsi_ver = master->core->ver;
u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
@@ -1200,10 +1299,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
fsi_reg_write(fsi, CKG2, data);
- /* set format */
- fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
- fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
-
/* spdif ? */
if (fsi_is_spdif(fsi)) {
fsi_spdif_clk_ctrl(fsi, 1);
@@ -1211,15 +1306,18 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
}
/*
- * FIXME
- *
- * FSI driver assumed that data package is in-back.
- * FSI2 chip can select it.
+ * get bus settings
*/
- if (fsi_ver >= 2) {
- fsi_reg_write(fsi, OUT_DMAC, (1 << 4));
- fsi_reg_write(fsi, IN_DMAC, (1 << 4));
+ data = 0;
+ switch (io->sample_width) {
+ case 2:
+ data = BUSOP_GET(16, io->bus_option);
+ break;
+ case 4:
+ data = BUSOP_GET(24, io->bus_option);
+ break;
}
+ fsi_format_bus_setup(fsi, io, data, dev);
/* irq clear */
fsi_irq_disable(fsi, io);
@@ -1243,7 +1341,9 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- return fsi_hw_startup(fsi, fsi_stream_get(fsi, substream), dai->dev);
+ fsi->rate = 0;
+
+ return 0;
}
static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
@@ -1251,7 +1351,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- fsi_hw_shutdown(fsi, dai->dev);
fsi->rate = 0;
}
@@ -1265,11 +1364,13 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
fsi_stream_init(fsi, io, substream);
+ fsi_hw_startup(fsi, io, dai->dev);
ret = fsi_stream_transfer(io);
if (0 == ret)
fsi_stream_start(fsi, io);
break;
case SNDRV_PCM_TRIGGER_STOP:
+ fsi_hw_shutdown(fsi, dai->dev);
fsi_stream_stop(fsi, io);
fsi_stream_quit(fsi, io);
break;
@@ -1280,42 +1381,33 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
{
- u32 data = 0;
-
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- data = CR_I2S;
+ fsi->fmt = CR_I2S;
fsi->chan_num = 2;
break;
case SND_SOC_DAIFMT_LEFT_J:
- data = CR_PCM;
+ fsi->fmt = CR_PCM;
fsi->chan_num = 2;
break;
default:
return -EINVAL;
}
- fsi->do_fmt = data;
- fsi->di_fmt = data;
-
return 0;
}
static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
{
struct fsi_master *master = fsi_get_master(fsi);
- u32 data = 0;
- if (master->core->ver < 2)
+ if (fsi_version(master) < 2)
return -EINVAL;
- data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+ fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM;
fsi->chan_num = 2;
fsi->spdif = 1;
- fsi->do_fmt = data;
- fsi->di_fmt = data;
-
return 0;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c88d9741b9e..b37ee8077ed 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -39,6 +39,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
@@ -54,7 +55,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
#endif
static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(card_list);
static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
@@ -465,6 +465,35 @@ static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
}
#endif
+struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
+ const char *dai_link, int stream)
+{
+ int i;
+
+ for (i = 0; i < card->num_links; i++) {
+ if (card->rtd[i].dai_link->no_pcm &&
+ !strcmp(card->rtd[i].dai_link->name, dai_link))
+ return card->rtd[i].pcm->streams[stream].substream;
+ }
+ dev_dbg(card->dev, "failed to find dai link %s\n", dai_link);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
+
+struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+ const char *dai_link)
+{
+ int i;
+
+ for (i = 0; i < card->num_links; i++) {
+ if (!strcmp(card->rtd[i].dai_link->name, dai_link))
+ return &card->rtd[i];
+ }
+ dev_dbg(card->dev, "failed to find rtd %s\n", dai_link);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
+
#ifdef CONFIG_SND_SOC_AC97_BUS
/* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -567,19 +596,16 @@ int snd_soc_suspend(struct device *dev)
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_PLAYBACK,
- codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_CAPTURE,
- codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
}
@@ -683,17 +709,16 @@ static void soc_resume_deferred(struct work_struct *work)
}
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
- SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+ SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_RESUME);
snd_soc_dapm_stream_event(&card->rtd[i],
- SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+ SNDRV_PCM_STREAM_CAPTURE,
SND_SOC_DAPM_STREAM_RESUME);
}
@@ -783,15 +808,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai *codec_dai, *cpu_dai;
const char *platform_name;
- if (rtd->complete)
- return 1;
dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
- /* do we already have the CPU DAI for this link ? */
- if (rtd->cpu_dai) {
- goto find_codec;
- }
- /* no, then find CPU DAI from registered DAIs*/
+ /* Find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) {
if (dai_link->cpu_dai_of_node) {
if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
@@ -802,18 +821,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
}
rtd->cpu_dai = cpu_dai;
- goto find_codec;
}
- dev_dbg(card->dev, "CPU DAI %s not registered\n",
- dai_link->cpu_dai_name);
-find_codec:
- /* do we already have the CODEC for this link ? */
- if (rtd->codec) {
- goto find_platform;
+ if (!rtd->cpu_dai) {
+ dev_dbg(card->dev, "CPU DAI %s not registered\n",
+ dai_link->cpu_dai_name);
+ return -EPROBE_DEFER;
}
- /* no, then find CODEC from registered CODECs*/
+ /* Find CODEC from registered CODECs */
list_for_each_entry(codec, &codec_list, list) {
if (dai_link->codec_of_node) {
if (codec->dev->of_node != dai_link->codec_of_node)
@@ -835,28 +851,28 @@ find_codec:
dai_link->codec_dai_name)) {
rtd->codec_dai = codec_dai;
- goto find_platform;
}
}
- dev_dbg(card->dev, "CODEC DAI %s not registered\n",
- dai_link->codec_dai_name);
- goto find_platform;
+ if (!rtd->codec_dai) {
+ dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+ dai_link->codec_dai_name);
+ return -EPROBE_DEFER;
+ }
}
- dev_dbg(card->dev, "CODEC %s not registered\n",
- dai_link->codec_name);
-find_platform:
- /* do we need a platform? */
- if (rtd->platform)
- goto out;
+ if (!rtd->codec) {
+ dev_dbg(card->dev, "CODEC %s not registered\n",
+ dai_link->codec_name);
+ return -EPROBE_DEFER;
+ }
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
if (!platform_name && !dai_link->platform_of_node)
platform_name = "snd-soc-dummy";
- /* no, then find one from the set of registered platforms */
+ /* find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
if (dai_link->platform_of_node) {
if (platform->dev->of_node !=
@@ -868,20 +884,16 @@ find_platform:
}
rtd->platform = platform;
- goto out;
}
-
- dev_dbg(card->dev, "platform %s not registered\n",
+ if (!rtd->platform) {
+ dev_dbg(card->dev, "platform %s not registered\n",
dai_link->platform_name);
- return 0;
-
-out:
- /* mark rtd as complete if we found all 4 of our client devices */
- if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
- rtd->complete = 1;
- card->num_rtd++;
+ return -EPROBE_DEFER;
}
- return 1;
+
+ card->num_rtd++;
+
+ return 0;
}
static void soc_remove_codec(struct snd_soc_codec *codec)
@@ -1068,6 +1080,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
{
int ret = 0;
const struct snd_soc_platform_driver *driver = platform->driver;
+ struct snd_soc_dai *dai;
platform->card = card;
platform->dapm.card = card;
@@ -1081,6 +1094,14 @@ static int soc_probe_platform(struct snd_soc_card *card,
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
+ /* Create DAPM widgets for each DAI stream */
+ list_for_each_entry(dai, &dai_list, list) {
+ if (dai->dev != platform->dev)
+ continue;
+
+ snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+ }
+
platform->dapm.idle_bias_off = 1;
if (driver->probe) {
@@ -1170,6 +1191,10 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev->init_name = name;
dev_set_drvdata(rtd->dev, rtd);
mutex_init(&rtd->pcm_mutex);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
+ INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_add(rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@@ -1191,6 +1216,17 @@ static int soc_post_component_init(struct snd_soc_card *card,
dev_err(codec->dev,
"asoc: failed to add codec sysfs files: %d\n", ret);
+#ifdef CONFIG_DEBUG_FS
+ /* add DPCM sysfs entries */
+ if (!dailess && !dai_link->dynamic)
+ goto out;
+
+ ret = soc_dpcm_debugfs_add(rtd);
+ if (ret < 0)
+ dev_err(rtd->dev, "asoc: failed to add dpcm sysfs entries: %d\n", ret);
+
+out:
+#endif
return 0;
}
@@ -1200,14 +1236,15 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dapm_widget *play_w, *capture_w;
int ret;
dev_dbg(card->dev, "probe %s dai link %d late %d\n",
card->name, num, order);
/* config components */
- codec_dai->codec = codec;
cpu_dai->platform = platform;
codec_dai->card = card;
cpu_dai->card = card;
@@ -1218,9 +1255,12 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
/* probe the cpu_dai */
if (!cpu_dai->probed &&
cpu_dai->driver->probe_order == order) {
+ cpu_dai->dapm.card = card;
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
+ snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
@@ -1279,12 +1319,39 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
if (ret < 0)
pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
- /* create the pcm */
- ret = soc_new_pcm(rtd, num);
- if (ret < 0) {
- pr_err("asoc: can't create pcm %s :%d\n",
- dai_link->stream_name, ret);
- return ret;
+ if (!dai_link->params) {
+ /* create the pcm */
+ ret = soc_new_pcm(rtd, num);
+ if (ret < 0) {
+ pr_err("asoc: can't create pcm %s :%d\n",
+ dai_link->stream_name, ret);
+ return ret;
+ }
+ } else {
+ /* link the DAI widgets */
+ play_w = codec_dai->playback_widget;
+ capture_w = cpu_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ capture_w, play_w);
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
+ }
+
+ play_w = cpu_dai->playback_widget;
+ capture_w = codec_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ capture_w, play_w);
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
+ }
}
/* add platform data for AC97 devices */
@@ -1334,6 +1401,20 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
}
#endif
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+ struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+ struct snd_soc_codec *codec;
+
+ /* find CODEC from registered CODECs*/
+ list_for_each_entry(codec, &codec_list, list) {
+ if (!strcmp(codec->name, aux_dev->codec_name))
+ return 0;
+ }
+
+ return -EPROBE_DEFER;
+}
+
static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
{
struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
@@ -1354,7 +1435,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
}
/* codec not found */
dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name);
- goto out;
+ return -EPROBE_DEFER;
found:
ret = soc_probe_codec(card, codec);
@@ -1404,29 +1485,28 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec,
return 0;
}
-static void snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
struct snd_soc_dai_link *dai_link;
- int ret, i, order;
+ int ret, i, order, dai_fmt;
- mutex_lock(&card->mutex);
-
- if (card->instantiated) {
- mutex_unlock(&card->mutex);
- return;
- }
+ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
- for (i = 0; i < card->num_links; i++)
- soc_bind_dai_link(card, i);
+ for (i = 0; i < card->num_links; i++) {
+ ret = soc_bind_dai_link(card, i);
+ if (ret != 0)
+ goto base_error;
+ }
- /* bind completed ? */
- if (card->num_rtd != card->num_links) {
- mutex_unlock(&card->mutex);
- return;
+ /* check aux_devs too */
+ for (i = 0; i < card->num_aux_devs; i++) {
+ ret = soc_check_aux_dev(card, i);
+ if (ret != 0)
+ goto base_error;
}
/* initialize the register cache for each available codec */
@@ -1446,10 +1526,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
ret = snd_soc_init_codec_cache(codec, compress_type);
- if (ret < 0) {
- mutex_unlock(&card->mutex);
- return;
- }
+ if (ret < 0)
+ goto base_error;
}
/* card bind complete so register a sound card */
@@ -1458,8 +1536,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
if (ret < 0) {
pr_err("asoc: can't create sound card for card %s: %d\n",
card->name, ret);
- mutex_unlock(&card->mutex);
- return;
+ goto base_error;
}
card->snd_card->dev = card->dev;
@@ -1523,17 +1600,47 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
for (i = 0; i < card->num_links; i++) {
dai_link = &card->dai_link[i];
+ dai_fmt = dai_link->dai_fmt;
- if (dai_link->dai_fmt) {
+ if (dai_fmt) {
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
- dai_link->dai_fmt);
+ dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].codec_dai->dev,
"Failed to set DAI format: %d\n",
ret);
+ }
+ /* If this is a regular CPU link there will be a platform */
+ if (dai_fmt &&
+ (dai_link->platform_name || dai_link->platform_of_node)) {
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
- dai_link->dai_fmt);
+ dai_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(card->rtd[i].cpu_dai->dev,
+ "Failed to set DAI format: %d\n",
+ ret);
+ } else if (dai_fmt) {
+ /* Flip the polarity for the "CPU" end */
+ dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+ switch (dai_link->dai_fmt &
+ SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ }
+
+ ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
+ dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"Failed to set DAI format: %d\n",
@@ -1599,7 +1706,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
- return;
+
+ return 0;
probe_aux_dev_err:
for (i = 0; i < card->num_aux_devs; i++)
@@ -1614,18 +1722,10 @@ card_probe_error:
snd_card_free(card->snd_card);
+base_error:
mutex_unlock(&card->mutex);
-}
-/*
- * Attempt to initialise any uninitialised cards. Must be called with
- * client_mutex.
- */
-static void snd_soc_instantiate_cards(void)
-{
- struct snd_soc_card *card;
- list_for_each_entry(card, &card_list, list)
- snd_soc_instantiate_card(card);
+ return ret;
}
/* probes a new socdev */
@@ -2527,6 +2627,87 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+
+ ucontrol->value.integer.value[0] =
+ ((snd_soc_read(codec, reg) >> shift) - min) & mask;
+
+ if (snd_soc_volsw_is_stereo(mc))
+ ucontrol->value.integer.value[1] =
+ ((snd_soc_read(codec, reg2) >> rshift) - min) & mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+ int err = 0;
+ unsigned short val, val_mask, val2 = 0;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] + min) & mask;
+ val = val << shift;
+
+ if (snd_soc_update_bits_locked(codec, reg, val_mask, val))
+ return err;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val_mask = mask << rshift;
+ val2 = (ucontrol->value.integer.value[1] + min) & mask;
+ val2 = val2 << rshift;
+
+ if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2))
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
* snd_soc_info_volsw_s8 - signed mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
@@ -2647,99 +2828,6 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-/**
- * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
- * mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int max = mc->max;
- int min = mc->min;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = max-min;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
-
-/**
- * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
- * mixer get callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int mask = (1<<mc->shift)-1;
- int min = mc->min;
- int val = snd_soc_read(codec, mc->reg) & mask;
- int valr = snd_soc_read(codec, mc->rreg) & mask;
-
- ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask;
- ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
-
-/**
- * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
- * mixer put callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int mask = (1<<mc->shift)-1;
- int min = mc->min;
- int ret;
- unsigned int val, valr, oval, ovalr;
-
- val = ((ucontrol->value.integer.value[0]+min) & 0xff);
- val &= mask;
- valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
- valr &= mask;
-
- oval = snd_soc_read(codec, mc->reg) & mask;
- ovalr = snd_soc_read(codec, mc->rreg) & mask;
-
- ret = 0;
- if (oval != val) {
- ret = snd_soc_write(codec, mc->reg, val);
- if (ret < 0)
- return ret;
- }
- if (ovalr != valr) {
- ret = snd_soc_write(codec, mc->rreg, valr);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
-
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2850,6 +2938,186 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = mc->min;
+ uinfo->value.integer.max = mc->max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long min = mc->min;
+ long max = mc->max;
+ long val = 0;
+ unsigned long regval;
+ unsigned int i;
+
+ for (i = 0; i < regcount; i++) {
+ regval = snd_soc_read(codec, regbase+i) & regwmask;
+ val |= regval << (regwshift*(regcount-i-1));
+ }
+ val &= mask;
+ if (min < 0 && val > max)
+ val |= ~mask;
+ if (invert)
+ val = max - val;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long max = mc->max;
+ long val = ucontrol->value.integer.value[0];
+ unsigned int i, regval, regmask;
+ int err;
+
+ if (invert)
+ val = max - val;
+ val &= mask;
+ for (i = 0; i < regcount; i++) {
+ regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+ regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+ err = snd_soc_update_bits_locked(codec, regbase+i,
+ regmask, regval);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int val = snd_soc_read(codec, reg) & mask;
+
+ if (shift != 0 && val != 0)
+ val = val >> shift;
+ ucontrol->value.enumerated.item[0] = val ^ invert;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+ unsigned int val1 = (strobe ^ invert) ? mask : 0;
+ unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+ int err;
+
+ err = snd_soc_update_bits_locked(codec, reg, mask, val1);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits_locked(codec, reg, mask, val2);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
+
+/**
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI
* @clk_id: DAI specific clock ID
@@ -3048,7 +3316,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
if (dai->driver && dai->driver->ops->digital_mute)
return dai->driver->ops->digital_mute(dai, mute);
else
- return -EINVAL;
+ return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
@@ -3060,7 +3328,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
- int i;
+ int i, ret;
if (!card->name || !card->dev)
return -EINVAL;
@@ -3123,15 +3391,13 @@ int snd_soc_register_card(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
mutex_init(&card->mutex);
+ mutex_init(&card->dapm_mutex);
- mutex_lock(&client_mutex);
- list_add(&card->list, &card_list);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
-
- dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+ ret = snd_soc_instantiate_card(card);
+ if (ret != 0)
+ soc_cleanup_card_debugfs(card);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
@@ -3145,9 +3411,6 @@ int snd_soc_unregister_card(struct snd_soc_card *card)
{
if (card->instantiated)
soc_cleanup_card_resources(card);
- mutex_lock(&client_mutex);
- list_del(&card->list);
- mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
return 0;
@@ -3221,6 +3484,7 @@ static inline char *fmt_multiple_name(struct device *dev,
int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
+ struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
dev_dbg(dev, "dai register %s\n", dev_name(dev));
@@ -3238,12 +3502,23 @@ int snd_soc_register_dai(struct device *dev,
dai->dev = dev;
dai->driver = dai_drv;
+ dai->dapm.dev = dev;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
+
+ list_for_each_entry(codec, &codec_list, list) {
+ if (codec->dev == dev) {
+ dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+ dai->name, codec->name);
+ dai->codec = codec;
+ break;
+ }
+ }
+
list_add(&dai->list, &dai_list);
- snd_soc_instantiate_cards();
+
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
@@ -3287,6 +3562,7 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
int snd_soc_register_dais(struct device *dev,
struct snd_soc_dai_driver *dai_drv, size_t count)
{
+ struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
int i, ret = 0;
@@ -3314,19 +3590,28 @@ int snd_soc_register_dais(struct device *dev,
dai->id = dai->driver->id;
else
dai->id = i;
+ dai->dapm.dev = dev;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
+
+ list_for_each_entry(codec, &codec_list, list) {
+ if (codec->dev == dev) {
+ dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+ dai->name, codec->name);
+ dai->codec = codec;
+ break;
+ }
+ }
+
list_add(&dai->list, &dai_list);
+
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
}
- mutex_lock(&client_mutex);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
return 0;
err:
@@ -3384,7 +3669,6 @@ int snd_soc_register_platform(struct device *dev,
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
- snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
@@ -3534,18 +3818,18 @@ int snd_soc_register_codec(struct device *dev,
fixup_codec_formats(&dai_drv[i].capture);
}
+ mutex_lock(&client_mutex);
+ list_add(&codec->list, &codec_list);
+ mutex_unlock(&client_mutex);
+
/* register any DAIs */
if (num_dai) {
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
if (ret < 0)
- goto fail;
+ dev_err(codec->dev, "Failed to regster DAIs: %d\n",
+ ret);
}
- mutex_lock(&client_mutex);
- list_add(&codec->list, &codec_list);
- snd_soc_instantiate_cards();
- mutex_unlock(&client_mutex);
-
pr_debug("Registered codec '%s'\n", codec->name);
return 0;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1bb6d4a63cd..90ee77d2409 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,6 +52,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_supply] = 1,
[snd_soc_dapm_regulator_supply] = 1,
[snd_soc_dapm_micbias] = 2,
+ [snd_soc_dapm_dai_link] = 2,
[snd_soc_dapm_dai] = 3,
[snd_soc_dapm_aif_in] = 3,
[snd_soc_dapm_aif_out] = 3,
@@ -90,9 +91,10 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_aif_in] = 10,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai] = 10,
- [snd_soc_dapm_regulator_supply] = 11,
- [snd_soc_dapm_supply] = 11,
- [snd_soc_dapm_post] = 12,
+ [snd_soc_dapm_dai_link] = 11,
+ [snd_soc_dapm_regulator_supply] = 12,
+ [snd_soc_dapm_supply] = 12,
+ [snd_soc_dapm_post] = 13,
};
static void pop_wait(u32 pop_time)
@@ -208,7 +210,23 @@ static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
return -1;
}
-static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+static inline void soc_widget_lock(struct snd_soc_dapm_widget *w)
+{
+ if (w->codec && !w->codec->using_regmap)
+ mutex_lock(&w->codec->mutex);
+ else if (w->platform)
+ mutex_lock(&w->platform->mutex);
+}
+
+static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w)
+{
+ if (w->codec && !w->codec->using_regmap)
+ mutex_unlock(&w->codec->mutex);
+ else if (w->platform)
+ mutex_unlock(&w->platform->mutex);
+}
+
+static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
unsigned short reg, unsigned int mask, unsigned int value)
{
bool change;
@@ -221,18 +239,24 @@ static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
if (ret != 0)
return ret;
} else {
+ soc_widget_lock(w);
ret = soc_widget_read(w, reg);
- if (ret < 0)
+ if (ret < 0) {
+ soc_widget_unlock(w);
return ret;
+ }
old = ret;
new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
ret = soc_widget_write(w, reg, new);
- if (ret < 0)
+ if (ret < 0) {
+ soc_widget_unlock(w);
return ret;
+ }
}
+ soc_widget_unlock(w);
}
return change;
@@ -374,6 +398,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mic:
case snd_soc_dapm_spk:
case snd_soc_dapm_line:
+ case snd_soc_dapm_dai_link:
p->connect = 1;
break;
/* does affect routing - dynamically connected */
@@ -682,11 +707,51 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
}
}
+/* add widget to list if it's not already in the list */
+static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list,
+ struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget_list *wlist;
+ int wlistsize, wlistentries, i;
+
+ if (*list == NULL)
+ return -EINVAL;
+
+ wlist = *list;
+
+ /* is this widget already in the list */
+ for (i = 0; i < wlist->num_widgets; i++) {
+ if (wlist->widgets[i] == w)
+ return 0;
+ }
+
+ /* allocate some new space */
+ wlistentries = wlist->num_widgets + 1;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *);
+ *list = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (*list == NULL) {
+ dev_err(w->dapm->dev, "can't allocate widget list for %s\n",
+ w->name);
+ return -ENOMEM;
+ }
+ wlist = *list;
+
+ /* insert the widget */
+ dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n",
+ w->name, wlist->num_widgets);
+
+ wlist->widgets[wlist->num_widgets] = w;
+ wlist->num_widgets++;
+ return 1;
+}
+
/*
* Recursively check for a completed path to an active or physically connected
* output widget. Returns number of complete paths.
*/
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_widget_list **list)
{
struct snd_soc_dapm_path *path;
int con = 0;
@@ -742,9 +807,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
if (path->walked)
continue;
+ trace_snd_soc_dapm_output_path(widget, path);
+
if (path->sink && path->connect) {
path->walked = 1;
- con += is_connected_output_ep(path->sink);
+
+ /* do we need to add this widget to the list ? */
+ if (list) {
+ int err;
+ err = dapm_list_add_widget(list, path->sink);
+ if (err < 0) {
+ dev_err(widget->dapm->dev, "could not add widget %s\n",
+ widget->name);
+ return con;
+ }
+ }
+
+ con += is_connected_output_ep(path->sink, list);
}
}
@@ -757,7 +836,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
* Recursively check for a completed path to an active or physically connected
* input widget. Returns number of complete paths.
*/
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_widget_list **list)
{
struct snd_soc_dapm_path *path;
int con = 0;
@@ -825,9 +905,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
if (path->walked)
continue;
+ trace_snd_soc_dapm_input_path(widget, path);
+
if (path->source && path->connect) {
path->walked = 1;
- con += is_connected_input_ep(path->source);
+
+ /* do we need to add this widget to the list ? */
+ if (list) {
+ int err;
+ err = dapm_list_add_widget(list, path->sink);
+ if (err < 0) {
+ dev_err(widget->dapm->dev, "could not add widget %s\n",
+ widget->name);
+ return con;
+ }
+ }
+
+ con += is_connected_input_ep(path->source, list);
}
}
@@ -836,6 +930,39 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
return con;
}
+/**
+ * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets.
+ * @dai: the soc DAI.
+ * @stream: stream direction.
+ * @list: list of active widgets for this stream.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the initial stream specified by name. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+ struct snd_soc_dapm_widget_list **list)
+{
+ struct snd_soc_card *card = dai->card;
+ int paths;
+
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ dapm_reset(card);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ paths = is_connected_output_ep(dai->playback_widget, list);
+ else
+ paths = is_connected_input_ep(dai->playback_widget, list);
+
+ trace_snd_soc_dapm_connected(paths, stream);
+ dapm_clear_walk(&card->dapm);
+ mutex_unlock(&card->dapm_mutex);
+
+ return paths;
+}
+
/*
* Handler for generic register modifier widget.
*/
@@ -849,7 +976,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
else
val = w->off_val;
- soc_widget_update_bits(w, -(w->reg + 1),
+ soc_widget_update_bits_locked(w, -(w->reg + 1),
w->mask << w->shift, val << w->shift);
return 0;
@@ -863,9 +990,9 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
- return regulator_enable(w->priv);
+ return regulator_enable(w->regulator);
else
- return regulator_disable_deferred(w->priv, w->shift);
+ return regulator_disable_deferred(w->regulator, w->shift);
}
EXPORT_SYMBOL_GPL(dapm_regulator_event);
@@ -892,9 +1019,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
- in = is_connected_input_ep(w);
+ in = is_connected_input_ep(w, NULL);
dapm_clear_walk(w->dapm);
- out = is_connected_output_ep(w);
+ out = is_connected_output_ep(w, NULL);
dapm_clear_walk(w->dapm);
return out != 0 && in != 0;
}
@@ -903,7 +1030,10 @@ static int dapm_dai_check_power(struct snd_soc_dapm_widget *w)
{
DAPM_UPDATE_STAT(w, power_checks);
- return w->active;
+ if (w->active)
+ return w->active;
+
+ return dapm_generic_check_power(w);
}
/* Check to see if an ADC has power */
@@ -914,7 +1044,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
if (w->active) {
- in = is_connected_input_ep(w);
+ in = is_connected_input_ep(w, NULL);
dapm_clear_walk(w->dapm);
return in != 0;
} else {
@@ -930,7 +1060,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
if (w->active) {
- out = is_connected_output_ep(w);
+ out = is_connected_output_ep(w, NULL);
dapm_clear_walk(w->dapm);
return out != 0;
} else {
@@ -1107,7 +1237,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
pop_wait(card->pop_time);
- soc_widget_update_bits(w, reg, mask, value);
+ soc_widget_update_bits_locked(w, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@@ -1237,7 +1367,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
w->name, ret);
}
- ret = snd_soc_update_bits(w->codec, update->reg, update->mask,
+ ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
update->val);
if (ret < 0)
pr_err("%s DAPM update failed: %d\n", w->name, ret);
@@ -1421,12 +1551,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
trace_snd_soc_dapm_start(card);
list_for_each_entry(d, &card->dapm_list, list) {
- if (d->n_widgets || d->codec == NULL) {
- if (d->idle_bias_off)
- d->target_bias_level = SND_SOC_BIAS_OFF;
- else
- d->target_bias_level = SND_SOC_BIAS_STANDBY;
- }
+ if (d->idle_bias_off)
+ d->target_bias_level = SND_SOC_BIAS_OFF;
+ else
+ d->target_bias_level = SND_SOC_BIAS_STANDBY;
}
dapm_reset(card);
@@ -1471,32 +1599,6 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
}
- /* If there are no DAPM widgets then try to figure out power from the
- * event type.
- */
- if (!dapm->n_widgets) {
- switch (event) {
- case SND_SOC_DAPM_STREAM_START:
- case SND_SOC_DAPM_STREAM_RESUME:
- dapm->target_bias_level = SND_SOC_BIAS_ON;
- break;
- case SND_SOC_DAPM_STREAM_STOP:
- if (dapm->codec && dapm->codec->active)
- dapm->target_bias_level = SND_SOC_BIAS_ON;
- else
- dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
- break;
- case SND_SOC_DAPM_STREAM_SUSPEND:
- dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
- break;
- case SND_SOC_DAPM_STREAM_NOP:
- dapm->target_bias_level = dapm->bias_level;
- break;
- default:
- break;
- }
- }
-
/* Force all contexts in the card to the same bias state if
* they're not ground referenced.
*/
@@ -1560,9 +1662,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (!buf)
return -ENOMEM;
- in = is_connected_input_ep(w);
+ in = is_connected_input_ep(w, NULL);
dapm_clear_walk(w->dapm);
- out = is_connected_output_ep(w);
+ out = is_connected_output_ep(w, NULL);
dapm_clear_walk(w->dapm);
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
@@ -1709,7 +1811,7 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
/* test and update the power status of a mux widget */
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
@@ -1746,12 +1848,26 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
}
- return 0;
+ return found;
+}
+
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+{
+ struct snd_soc_card *card = widget->dapm->card;
+ int ret;
+
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ mutex_unlock(&card->dapm_mutex);
+ if (ret > 0)
+ soc_dpcm_runtime_update(widget);
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int connect)
{
struct snd_soc_dapm_path *path;
@@ -1778,7 +1894,21 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
}
- return 0;
+ return found;
+}
+
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol, int connect)
+{
+ struct snd_soc_card *card = widget->dapm->card;
+ int ret;
+
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ mutex_unlock(&card->dapm_mutex);
+ if (ret > 0)
+ soc_dpcm_runtime_update(widget);
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
@@ -1939,6 +2069,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
*/
int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
{
+ int ret;
+
/*
* Suppress early reports (eg, jacks syncing their state) to avoid
* silly DAPM runs during card startup.
@@ -1946,7 +2078,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
if (!dapm->card || !dapm->card->instantiated)
return 0;
- return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+ mutex_unlock(&dapm->card->dapm_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
@@ -2055,6 +2190,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
+ case snd_soc_dapm_dai_link:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
@@ -2110,19 +2246,21 @@ err:
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
- int i, ret;
+ int i, ret = 0;
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
ret = snd_soc_dapm_add_route(dapm, route);
if (ret < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
- return ret;
+ break;
}
route++;
}
+ mutex_unlock(&dapm->card->dapm_mutex);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
@@ -2193,12 +2331,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
int i, err;
int ret = 0;
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
err = snd_soc_dapm_weak_route(dapm, route);
if (err)
ret = err;
route++;
}
+ mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
@@ -2217,6 +2357,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
struct snd_soc_dapm_widget *w;
unsigned int val;
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+
list_for_each_entry(w, &dapm->card->widgets, list)
{
if (w->new)
@@ -2226,8 +2368,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
w->kcontrols = kzalloc(w->num_kcontrols *
sizeof(struct snd_kcontrol *),
GFP_KERNEL);
- if (!w->kcontrols)
+ if (!w->kcontrols) {
+ mutex_unlock(&dapm->card->dapm_mutex);
return -ENOMEM;
+ }
}
switch(w->id) {
@@ -2267,6 +2411,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
}
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+ mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -2326,6 +2471,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_card *card = codec->card;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -2352,7 +2498,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
/* old connection must be powered down */
connect = invert ? 1 : 0;
- mutex_lock(&codec->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
@@ -2368,13 +2514,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
update.val = val;
widget->dapm->update = &update;
- snd_soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ soc_dapm_mixer_update_power(widget, kcontrol, connect);
widget->dapm->update = NULL;
}
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&card->dapm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -2423,6 +2569,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_card *card = codec->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask, bitmask;
@@ -2443,7 +2590,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mask |= (bitmask - 1) << e->shift_r;
}
- mutex_lock(&codec->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
@@ -2459,13 +2606,13 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
widget->dapm->update = &update;
- snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ soc_dapm_mux_update_power(widget, kcontrol, mux, e);
widget->dapm->update = NULL;
}
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&card->dapm_mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -2502,6 +2649,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_card *card = codec->card;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
@@ -2511,7 +2659,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
- mutex_lock(&codec->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = widget->value != ucontrol->value.enumerated.item[0];
if (change) {
@@ -2520,11 +2668,11 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
widget->value = ucontrol->value.enumerated.item[0];
- snd_soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
+ soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
}
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&card->dapm_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2589,6 +2737,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_card *card = codec->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
@@ -2607,7 +2756,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mask |= e->mask << e->shift_r;
}
- mutex_lock(&codec->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
@@ -2623,13 +2772,13 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
widget->dapm->update = &update;
- snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ soc_dapm_mux_update_power(widget, kcontrol, mux, e);
widget->dapm->update = NULL;
}
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&card->dapm_mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2666,12 +2815,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
const char *pin = (const char *)kcontrol->private_value;
- mutex_lock(&card->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
ucontrol->value.integer.value[0] =
snd_soc_dapm_get_pin_status(&card->dapm, pin);
- mutex_unlock(&card->mutex);
+ mutex_unlock(&card->dapm_mutex);
return 0;
}
@@ -2689,17 +2838,16 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
const char *pin = (const char *)kcontrol->private_value;
- mutex_lock(&card->mutex);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (ucontrol->value.integer.value[0])
snd_soc_dapm_enable_pin(&card->dapm, pin);
else
snd_soc_dapm_disable_pin(&card->dapm, pin);
- snd_soc_dapm_sync(&card->dapm);
-
- mutex_unlock(&card->mutex);
+ mutex_unlock(&card->dapm_mutex);
+ snd_soc_dapm_sync(&card->dapm);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
@@ -2717,9 +2865,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
switch (w->id) {
case snd_soc_dapm_regulator_supply:
- w->priv = devm_regulator_get(dapm->dev, w->name);
- if (IS_ERR(w->priv)) {
- ret = PTR_ERR(w->priv);
+ w->regulator = devm_regulator_get(dapm->dev, w->name);
+ if (IS_ERR(w->regulator)) {
+ ret = PTR_ERR(w->regulator);
dev_err(dapm->dev, "Failed to request %s: %d\n",
w->name, ret);
return NULL;
@@ -2771,6 +2919,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
+ case snd_soc_dapm_dai_link:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
@@ -2816,21 +2965,177 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
{
struct snd_soc_dapm_widget *w;
int i;
+ int ret = 0;
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
w = snd_soc_dapm_new_control(dapm, widget);
if (!w) {
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s\n",
widget->name);
- return -ENOMEM;
+ ret = -ENOMEM;
+ break;
}
widget++;
}
- return 0;
+ mutex_unlock(&dapm->card->dapm_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_dapm_path *source_p, *sink_p;
+ struct snd_soc_dai *source, *sink;
+ const struct snd_soc_pcm_stream *config = w->params;
+ struct snd_pcm_substream substream;
+ struct snd_pcm_hw_params *params = NULL;
+ u64 fmt;
+ int ret;
+
+ BUG_ON(!config);
+ BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks));
+
+ /* We only support a single source and sink, pick the first */
+ source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path,
+ list_sink);
+ sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path,
+ list_source);
+
+ BUG_ON(!source_p || !sink_p);
+ BUG_ON(!sink_p->source || !source_p->sink);
+ BUG_ON(!source_p->source || !sink_p->sink);
+
+ source = source_p->source->priv;
+ sink = sink_p->sink->priv;
+
+ /* Be a little careful as we don't want to overflow the mask array */
+ if (config->formats) {
+ fmt = ffs(config->formats) - 1;
+ } else {
+ dev_warn(w->dapm->dev, "Invalid format %llx specified\n",
+ config->formats);
+ fmt = 0;
+ }
+
+ /* Currently very limited parameter selection */
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
+
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
+ config->rate_min;
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =
+ config->rate_max;
+
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
+ = config->channels_min;
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
+ = config->channels_max;
+
+ memset(&substream, 0, sizeof(substream));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (source->driver->ops && source->driver->ops->hw_params) {
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ ret = source->driver->ops->hw_params(&substream,
+ params, source);
+ if (ret != 0) {
+ dev_err(source->dev,
+ "hw_params() failed: %d\n", ret);
+ goto out;
+ }
+ }
+
+ if (sink->driver->ops && sink->driver->ops->hw_params) {
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ ret = sink->driver->ops->hw_params(&substream, params,
+ sink);
+ if (ret != 0) {
+ dev_err(sink->dev,
+ "hw_params() failed: %d\n", ret);
+ goto out;
+ }
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_dai_digital_mute(sink, 0);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(sink->dev, "Failed to unmute: %d\n", ret);
+ ret = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = snd_soc_dai_digital_mute(sink, 1);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(sink->dev, "Failed to mute: %d\n", ret);
+ ret = 0;
+ break;
+
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+out:
+ kfree(params);
+ return ret;
+}
+
+int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
+ const struct snd_soc_pcm_stream *params,
+ struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_route routes[2];
+ struct snd_soc_dapm_widget template;
+ struct snd_soc_dapm_widget *w;
+ size_t len;
+ char *link_name;
+
+ len = strlen(source->name) + strlen(sink->name) + 2;
+ link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
+ if (!link_name)
+ return -ENOMEM;
+ snprintf(link_name, len, "%s-%s", source->name, sink->name);
+
+ memset(&template, 0, sizeof(template));
+ template.reg = SND_SOC_NOPM;
+ template.id = snd_soc_dapm_dai_link;
+ template.name = link_name;
+ template.event = snd_soc_dai_link_event;
+ template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD;
+
+ dev_dbg(card->dev, "adding %s widget\n", link_name);
+
+ w = snd_soc_dapm_new_control(&card->dapm, &template);
+ if (!w) {
+ dev_err(card->dev, "Failed to create %s widget\n",
+ link_name);
+ return -ENOMEM;
+ }
+
+ w->params = params;
+
+ memset(&routes, 0, sizeof(routes));
+
+ routes[0].source = source->name;
+ routes[0].sink = link_name;
+ routes[1].source = link_name;
+ routes[1].sink = sink->name;
+
+ return snd_soc_dapm_add_routes(&card->dapm, routes,
+ ARRAY_SIZE(routes));
+}
+
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *dai)
{
@@ -2934,37 +3239,61 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
return 0;
}
-static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
- int stream, struct snd_soc_dai *dai,
- int event)
+static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+ int event)
{
- struct snd_soc_dapm_widget *w;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+ struct snd_soc_dapm_widget *w_cpu, *w_codec;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
- if (!w)
- return;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w_cpu = cpu_dai->playback_widget;
+ w_codec = codec_dai->playback_widget;
+ } else {
+ w_cpu = cpu_dai->capture_widget;
+ w_codec = codec_dai->capture_widget;
+ }
- dapm_mark_dirty(w, "stream event");
+ if (w_cpu) {
- switch (event) {
- case SND_SOC_DAPM_STREAM_START:
- w->active = 1;
- break;
- case SND_SOC_DAPM_STREAM_STOP:
- w->active = 0;
- break;
- case SND_SOC_DAPM_STREAM_SUSPEND:
- case SND_SOC_DAPM_STREAM_RESUME:
- case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
- case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
- break;
+ dapm_mark_dirty(w_cpu, "stream event");
+
+ switch (event) {
+ case SND_SOC_DAPM_STREAM_START:
+ w_cpu->active = 1;
+ break;
+ case SND_SOC_DAPM_STREAM_STOP:
+ w_cpu->active = 0;
+ break;
+ case SND_SOC_DAPM_STREAM_SUSPEND:
+ case SND_SOC_DAPM_STREAM_RESUME:
+ case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+ case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+ break;
+ }
+ }
+
+ if (w_codec) {
+
+ dapm_mark_dirty(w_codec, "stream event");
+
+ switch (event) {
+ case SND_SOC_DAPM_STREAM_START:
+ w_codec->active = 1;
+ break;
+ case SND_SOC_DAPM_STREAM_STOP:
+ w_codec->active = 0;
+ break;
+ case SND_SOC_DAPM_STREAM_SUSPEND:
+ case SND_SOC_DAPM_STREAM_RESUME:
+ case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+ case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+ break;
+ }
}
- dapm_power_widgets(dapm, event);
+ dapm_power_widgets(&rtd->card->dapm, event);
}
/**
@@ -2978,15 +3307,14 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
*
* Returns 0 for success else error.
*/
-int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
- struct snd_soc_dai *dai, int event)
+void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+ int event)
{
- struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = rtd->card;
- mutex_lock(&codec->mutex);
- soc_dapm_stream_event(&codec->dapm, stream, dai, event);
- mutex_unlock(&codec->mutex);
- return 0;
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ soc_dapm_stream_event(rtd, stream, event);
+ mutex_unlock(&card->dapm_mutex);
}
/**
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index ee4353f843e..7f8b3b7428b 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -36,6 +36,7 @@
int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
struct snd_soc_jack *jack)
{
+ mutex_init(&jack->mutex);
jack->codec = codec;
INIT_LIST_HEAD(&jack->pins);
INIT_LIST_HEAD(&jack->jack_zones);
@@ -75,7 +76,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
codec = jack->codec;
dapm = &codec->dapm;
- mutex_lock(&codec->mutex);
+ mutex_lock(&jack->mutex);
oldstatus = jack->status;
@@ -109,7 +110,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
snd_jack_report(jack->jack, jack->status);
out:
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&jack->mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_report);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0ad8dcacd2f..bedd1717a37 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -22,12 +22,38 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
#include <sound/initval.h>
+#define DPCM_MAX_BE_USERS 8
+
+/* 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)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+
+ dev_dbg(be->dev, "pm: BE %s event %d dir %d\n",
+ be->dai_link->name, event, dir);
+
+ snd_soc_dapm_stream_event(be, dir, event);
+ }
+
+ snd_soc_dapm_stream_event(fe, dir, event);
+
+ return 0;
+}
+
static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
struct snd_soc_dai *soc_dai)
{
@@ -156,6 +182,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
}
+ /* Dynamic PCM DAI links compat checks use dynamic capabilities */
+ if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
+ goto dynamic;
+
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
@@ -248,6 +278,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
+dynamic:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
codec_dai->playback_active++;
@@ -308,7 +339,7 @@ static void close_delayed_work(struct work_struct *work)
if (codec_dai->pop_wait == 1) {
codec_dai->pop_wait = 0;
snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
- codec_dai, SND_SOC_DAPM_STREAM_STOP);
+ SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&rtd->pcm_mutex);
@@ -373,7 +404,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
/* powered down playback stream now */
snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_PLAYBACK,
- codec_dai,
SND_SOC_DAPM_STREAM_STOP);
} else {
/* start delayed pop wq here for playback streams */
@@ -384,7 +414,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
- codec_dai, SND_SOC_DAPM_STREAM_STOP);
+ SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&rtd->pcm_mutex);
@@ -453,8 +483,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
cancel_delayed_work(&rtd->delayed_work);
}
- snd_soc_dapm_stream_event(rtd, substream->stream, codec_dai,
- SND_SOC_DAPM_STREAM_START);
+ snd_soc_dapm_stream_event(rtd, substream->stream,
+ SND_SOC_DAPM_STREAM_START);
snd_soc_dai_digital_mute(codec_dai, 0);
@@ -602,6 +632,34 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
+static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ if (codec_dai->driver->ops->bespoke_trigger) {
+ ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (platform->driver->bespoke_trigger) {
+ ret = platform->driver->bespoke_trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_dai->driver->ops->bespoke_trigger) {
+ ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
/*
* soc level wrapper for pointer callback
* If cpu_dai, codec_dai, platform driver has the delay callback, than
@@ -634,6 +692,1308 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
return offset;
}
+/* connect a FE and BE */
+static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ /* only add new dpcms */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ if (dpcm->be == be && dpcm->fe == fe)
+ return 0;
+ }
+
+ dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
+ if (!dpcm)
+ return -ENOMEM;
+
+ dpcm->be = be;
+ dpcm->fe = fe;
+ be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
+ dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
+ list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
+ list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
+
+ dev_dbg(fe->dev, " connected new DPCM %s path %s %s %s\n",
+ stream ? "capture" : "playback", fe->dai_link->name,
+ stream ? "<-" : "->", be->dai_link->name);
+
+#ifdef CONFIG_DEBUG_FS
+ dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
+ fe->debugfs_dpcm_root, &dpcm->state);
+#endif
+ return 1;
+}
+
+/* reparent a BE onto another FE */
+static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ struct snd_pcm_substream *fe_substream, *be_substream;
+
+ /* reparent if BE is connected to other FEs */
+ if (!be->dpcm[stream].users)
+ return;
+
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+ list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+ if (dpcm->fe == fe)
+ continue;
+
+ dev_dbg(fe->dev, " reparent %s path %s %s %s\n",
+ stream ? "capture" : "playback",
+ dpcm->fe->dai_link->name,
+ stream ? "<-" : "->", dpcm->be->dai_link->name);
+
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
+ be_substream->runtime = fe_substream->runtime;
+ break;
+ }
+}
+
+/* disconnect a BE and FE */
+static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm, *d;
+
+ list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) {
+ dev_dbg(fe->dev, "BE %s disconnect check for %s\n",
+ stream ? "capture" : "playback",
+ dpcm->be->dai_link->name);
+
+ if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
+ continue;
+
+ dev_dbg(fe->dev, " freed DSP %s path %s %s %s\n",
+ stream ? "capture" : "playback", fe->dai_link->name,
+ stream ? "<-" : "->", dpcm->be->dai_link->name);
+
+ /* BEs still alive need new FE */
+ dpcm_be_reparent(fe, dpcm->be, stream);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(dpcm->debugfs_state);
+#endif
+ list_del(&dpcm->list_be);
+ list_del(&dpcm->list_fe);
+ kfree(dpcm);
+ }
+}
+
+/* get BE for DAI widget and stream */
+static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
+ struct snd_soc_dapm_widget *widget, int stream)
+{
+ struct snd_soc_pcm_runtime *be;
+ int i;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < card->num_links; i++) {
+ be = &card->rtd[i];
+
+ if (be->cpu_dai->playback_widget == widget ||
+ be->codec_dai->playback_widget == widget)
+ return be;
+ }
+ } else {
+
+ for (i = 0; i < card->num_links; i++) {
+ be = &card->rtd[i];
+
+ if (be->cpu_dai->capture_widget == widget ||
+ be->codec_dai->capture_widget == widget)
+ return be;
+ }
+ }
+
+ dev_err(card->dev, "can't get %s BE for %s\n",
+ stream ? "capture" : "playback", widget->name);
+ return NULL;
+}
+
+static inline struct snd_soc_dapm_widget *
+ rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return rtd->cpu_dai->playback_widget;
+ else
+ return rtd->cpu_dai->capture_widget;
+}
+
+static inline struct snd_soc_dapm_widget *
+ rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return rtd->codec_dai->playback_widget;
+ else
+ return rtd->codec_dai->capture_widget;
+}
+
+static int widget_in_list(struct snd_soc_dapm_widget_list *list,
+ struct snd_soc_dapm_widget *widget)
+{
+ int i;
+
+ for (i = 0; i < list->num_widgets; i++) {
+ if (widget == list->widgets[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
+ int stream, struct snd_soc_dapm_widget_list **list_)
+{
+ struct snd_soc_dai *cpu_dai = fe->cpu_dai;
+ struct snd_soc_dapm_widget_list *list;
+ int paths;
+
+ list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
+ sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
+ if (list == NULL)
+ return -ENOMEM;
+
+ /* get number of valid DAI paths and their widgets */
+ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list);
+
+ dev_dbg(fe->dev, "found %d audio %s paths\n", paths,
+ stream ? "capture" : "playback");
+
+ *list_ = list;
+ return paths;
+}
+
+static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
+{
+ kfree(*list);
+}
+
+static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
+ struct snd_soc_dapm_widget_list **list_)
+{
+ struct snd_soc_dpcm *dpcm;
+ struct snd_soc_dapm_widget_list *list = *list_;
+ struct snd_soc_dapm_widget *widget;
+ int prune = 0;
+
+ /* Destroy any old FE <--> BE connections */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ /* is there a valid CPU DAI widget for this BE */
+ widget = rtd_get_cpu_widget(dpcm->be, stream);
+
+ /* prune the BE if it's no longer in our active list */
+ if (widget && widget_in_list(list, widget))
+ continue;
+
+ /* is there a valid CODEC DAI widget for this BE */
+ widget = rtd_get_codec_widget(dpcm->be, stream);
+
+ /* prune the BE if it's no longer in our active list */
+ if (widget && widget_in_list(list, widget))
+ continue;
+
+ dev_dbg(fe->dev, "pruning %s BE %s for %s\n",
+ stream ? "capture" : "playback",
+ dpcm->be->dai_link->name, fe->dai_link->name);
+ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+ dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ prune++;
+ }
+
+ dev_dbg(fe->dev, "found %d old BE paths for pruning\n", prune);
+ return prune;
+}
+
+static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
+ struct snd_soc_dapm_widget_list **list_)
+{
+ struct snd_soc_card *card = fe->card;
+ struct snd_soc_dapm_widget_list *list = *list_;
+ struct snd_soc_pcm_runtime *be;
+ int i, new = 0, err;
+
+ /* Create any new FE <--> BE connections */
+ for (i = 0; i < list->num_widgets; i++) {
+
+ if (list->widgets[i]->id != snd_soc_dapm_dai)
+ continue;
+
+ /* is there a valid BE rtd for this widget */
+ be = dpcm_get_be(card, list->widgets[i], stream);
+ if (!be) {
+ dev_err(fe->dev, "no BE found for %s\n",
+ list->widgets[i]->name);
+ continue;
+ }
+
+ /* make sure BE is a real BE */
+ if (!be->dai_link->no_pcm)
+ continue;
+
+ /* don't connect if FE is not running */
+ if (!fe->dpcm[stream].runtime)
+ continue;
+
+ /* newly connected FE and BE */
+ err = dpcm_be_connect(fe, be, stream);
+ if (err < 0) {
+ dev_err(fe->dev, "can't connect %s\n",
+ list->widgets[i]->name);
+ break;
+ } else if (err == 0) /* already connected */
+ continue;
+
+ /* new */
+ be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ new++;
+ }
+
+ dev_dbg(fe->dev, "found %d new BE paths\n", new);
+ return new;
+}
+
+/*
+ * Find the corresponding BE DAIs that source or sink audio to this
+ * FE substream.
+ */
+static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
+ int stream, struct snd_soc_dapm_widget_list **list, int new)
+{
+ if (new)
+ return dpcm_add_paths(fe, stream, list);
+ else
+ return dpcm_prune_paths(fe, stream, list);
+}
+
+static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+ dpcm->be->dpcm[stream].runtime_update =
+ SND_SOC_DPCM_UPDATE_NO;
+}
+
+static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
+ int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ /* disable any enabled and non active backends */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ if (be->dpcm[stream].users == 0)
+ dev_err(be->dev, "no users %s at close - state %d\n",
+ stream ? "capture" : "playback",
+ be->dpcm[stream].state);
+
+ if (--be->dpcm[stream].users != 0)
+ continue;
+
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
+ continue;
+
+ soc_pcm_close(be_substream);
+ be_substream->runtime = NULL;
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+ }
+}
+
+static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ int err, count = 0;
+
+ /* only startup BE DAIs that are either sinks or sources to this FE DAI */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ /* first time the dpcm is open ? */
+ if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
+ dev_err(be->dev, "too many users %s at open %d\n",
+ stream ? "capture" : "playback",
+ be->dpcm[stream].state);
+
+ if (be->dpcm[stream].users++ != 0)
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
+ continue;
+
+ dev_dbg(be->dev, "dpcm: open BE %s\n", be->dai_link->name);
+
+ be_substream->runtime = be->dpcm[stream].runtime;
+ err = soc_pcm_open(be_substream);
+ if (err < 0) {
+ dev_err(be->dev, "BE open failed %d\n", err);
+ be->dpcm[stream].users--;
+ if (be->dpcm[stream].users < 0)
+ dev_err(be->dev, "no users %s at unwind %d\n",
+ stream ? "capture" : "playback",
+ be->dpcm[stream].state);
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+ goto unwind;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
+ count++;
+ }
+
+ return count;
+
+unwind:
+ /* disable any enabled and non active backends */
+ list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ if (be->dpcm[stream].users == 0)
+ dev_err(be->dev, "no users %s at close %d\n",
+ stream ? "capture" : "playback",
+ be->dpcm[stream].state);
+
+ if (--be->dpcm[stream].users != 0)
+ continue;
+
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
+ continue;
+
+ soc_pcm_close(be_substream);
+ be_substream->runtime = NULL;
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+ }
+
+ return err;
+}
+
+static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
+ runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
+ runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
+ runtime->hw.channels_max = cpu_dai_drv->playback.channels_max;
+ runtime->hw.formats &= cpu_dai_drv->playback.formats;
+ runtime->hw.rates = cpu_dai_drv->playback.rates;
+ } else {
+ runtime->hw.rate_min = cpu_dai_drv->capture.rate_min;
+ runtime->hw.rate_max = cpu_dai_drv->capture.rate_max;
+ runtime->hw.channels_min = cpu_dai_drv->capture.channels_min;
+ runtime->hw.channels_max = cpu_dai_drv->capture.channels_max;
+ runtime->hw.formats &= cpu_dai_drv->capture.formats;
+ runtime->hw.rates = cpu_dai_drv->capture.rates;
+ }
+}
+
+static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
+{
+ struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ struct snd_pcm_runtime *runtime = fe_substream->runtime;
+ int stream = fe_substream->stream, ret = 0;
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ ret = dpcm_be_dai_startup(fe, fe_substream->stream);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: failed to start some BEs %d\n", ret);
+ goto be_err;
+ }
+
+ dev_dbg(fe->dev, "dpcm: open FE %s\n", fe->dai_link->name);
+
+ /* start the DAI frontend */
+ ret = soc_pcm_open(fe_substream);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: failed to start FE %d\n", ret);
+ goto unwind;
+ }
+
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
+
+ dpcm_set_fe_runtime(fe_substream);
+ snd_pcm_limit_hw_rates(runtime);
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ return 0;
+
+unwind:
+ dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
+be_err:
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ return ret;
+}
+
+static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ /* only shutdown BEs that are either sinks or sources to this FE DAI */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ if (be->dpcm[stream].users == 0)
+ dev_err(be->dev, "no users %s at close - state %d\n",
+ stream ? "capture" : "playback",
+ be->dpcm[stream].state);
+
+ if (--be->dpcm[stream].users != 0)
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN))
+ continue;
+
+ dev_dbg(be->dev, "dpcm: close BE %s\n",
+ dpcm->fe->dai_link->name);
+
+ soc_pcm_close(be_substream);
+ be_substream->runtime = NULL;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+ }
+ return 0;
+}
+
+static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ /* shutdown the BEs */
+ dpcm_be_dai_shutdown(fe, substream->stream);
+
+ dev_dbg(fe->dev, "dpcm: close FE %s\n", fe->dai_link->name);
+
+ /* now shutdown the frontend */
+ soc_pcm_close(substream);
+
+ /* run the stream event for each BE */
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ return 0;
+}
+
+static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+
+ /* only hw_params backends that are either sinks or sources
+ * to this frontend DAI */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ /* only free hw when no longer used - check all FEs */
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ dev_dbg(be->dev, "dpcm: hw_free BE %s\n",
+ dpcm->fe->dai_link->name);
+
+ soc_pcm_hw_free(be_substream);
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+ }
+
+ return 0;
+}
+
+static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int err, stream = substream->stream;
+
+ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ dev_dbg(fe->dev, "dpcm: hw_free FE %s\n", fe->dai_link->name);
+
+ /* call hw_free on the frontend */
+ err = soc_pcm_hw_free(substream);
+ if (err < 0)
+ dev_err(fe->dev,"dpcm: hw_free FE %s failed\n",
+ fe->dai_link->name);
+
+ /* only hw_params backends that are either sinks or sources
+ * to this frontend DAI */
+ err = dpcm_be_dai_hw_free(fe, stream);
+
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+ mutex_unlock(&fe->card->mutex);
+ return 0;
+}
+
+static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ int ret;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ /* only allow hw_params() if no connected FEs are running */
+ if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+ continue;
+
+ dev_dbg(be->dev, "dpcm: hw_params BE %s\n",
+ dpcm->fe->dai_link->name);
+
+ /* copy params for each dpcm */
+ memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
+ sizeof(struct snd_pcm_hw_params));
+
+ /* perform any hw_params fixups */
+ if (be->dai_link->be_hw_params_fixup) {
+ ret = be->dai_link->be_hw_params_fixup(be,
+ &dpcm->hw_params);
+ if (ret < 0) {
+ dev_err(be->dev,
+ "dpcm: hw_params BE fixup failed %d\n",
+ ret);
+ goto unwind;
+ }
+ }
+
+ ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
+ if (ret < 0) {
+ dev_err(dpcm->be->dev,
+ "dpcm: hw_params BE failed %d\n", ret);
+ goto unwind;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+ }
+ return 0;
+
+unwind:
+ /* disable any enabled and non active backends */
+ list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ /* only allow hw_free() if no connected FEs are running */
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ soc_pcm_hw_free(be_substream);
+ }
+
+ return ret;
+}
+
+static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int ret, stream = substream->stream;
+
+ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ memcpy(&fe->dpcm[substream->stream].hw_params, params,
+ sizeof(struct snd_pcm_hw_params));
+ ret = dpcm_be_dai_hw_params(fe, substream->stream);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: hw_params BE failed %d\n", ret);
+ goto out;
+ }
+
+ dev_dbg(fe->dev, "dpcm: hw_params FE %s rate %d chan %x fmt %d\n",
+ fe->dai_link->name, params_rate(params),
+ params_channels(params), params_format(params));
+
+ /* call hw_params on the frontend */
+ ret = soc_pcm_hw_params(substream, params);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: hw_params FE failed %d\n", ret);
+ dpcm_be_dai_hw_free(fe, stream);
+ } else
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+
+out:
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ mutex_unlock(&fe->card->mutex);
+ return ret;
+}
+
+static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret;
+
+ dev_dbg(dpcm->be->dev, "dpcm: trigger BE %s cmd %d\n",
+ dpcm->fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+ if (ret < 0)
+ dev_err(dpcm->be->dev,"dpcm: trigger BE failed %d\n", ret);
+
+ return ret;
+}
+
+static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+ int cmd)
+{
+ struct snd_soc_dpcm *dpcm;
+ int ret = 0;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+ continue;
+
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)
+ continue;
+
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+ continue;
+
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
+
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream, ret;
+ enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ switch (trigger) {
+ case SND_SOC_DPCM_TRIGGER_PRE:
+ /* call trigger on the frontend before the backend. */
+
+ dev_dbg(fe->dev, "dpcm: pre trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+ goto out;
+ }
+
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ break;
+ case SND_SOC_DPCM_TRIGGER_POST:
+ /* call trigger on the frontend after the backend. */
+
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+ goto out;
+ }
+
+ dev_dbg(fe->dev, "dpcm: post trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+ break;
+ case SND_SOC_DPCM_TRIGGER_BESPOKE:
+ /* bespoke trigger() - handles both FE and BEs */
+
+ dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_bespoke_trigger(substream, cmd);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+ goto out;
+ }
+ break;
+ default:
+ dev_err(fe->dev, "dpcm: invalid trigger cmd %d for %s\n", cmd,
+ fe->dai_link->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+ break;
+ }
+
+out:
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ return ret;
+}
+
+static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ int ret = 0;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ dev_dbg(be->dev, "dpcm: prepare BE %s\n",
+ dpcm->fe->dai_link->name);
+
+ ret = soc_pcm_prepare(be_substream);
+ if (ret < 0) {
+ dev_err(be->dev, "dpcm: backend prepare failed %d\n",
+ ret);
+ break;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+ }
+ return ret;
+}
+
+static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream, ret = 0;
+
+ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+
+ dev_dbg(fe->dev, "dpcm: prepare FE %s\n", fe->dai_link->name);
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ /* there is no point preparing this FE if there are no BEs */
+ if (list_empty(&fe->dpcm[stream].be_clients)) {
+ dev_err(fe->dev, "dpcm: no backend DAIs enabled for %s\n",
+ fe->dai_link->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = dpcm_be_dai_prepare(fe, substream->stream);
+ if (ret < 0)
+ goto out;
+
+ /* call prepare on the frontend */
+ ret = soc_pcm_prepare(substream);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: prepare FE %s failed\n",
+ fe->dai_link->name);
+ goto out;
+ }
+
+ /* run the stream event for each BE */
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
+ fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+
+out:
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ mutex_unlock(&fe->card->mutex);
+
+ return ret;
+}
+
+static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+
+ if (platform->driver->ops->ioctl)
+ return platform->driver->ops->ioctl(substream, cmd, arg);
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+ enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+ int err;
+
+ dev_dbg(fe->dev, "runtime %s close on FE %s\n",
+ stream ? "capture" : "playback", fe->dai_link->name);
+
+ if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
+ /* call bespoke trigger - FE takes care of all BE triggers */
+ dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd stop\n",
+ fe->dai_link->name);
+
+ err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+ if (err < 0)
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err);
+ } else {
+ dev_dbg(fe->dev, "dpcm: trigger FE %s cmd stop\n",
+ fe->dai_link->name);
+
+ err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
+ if (err < 0)
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err);
+ }
+
+ err = dpcm_be_dai_hw_free(fe, stream);
+ if (err < 0)
+ dev_err(fe->dev,"dpcm: hw_free FE failed %d\n", err);
+
+ err = dpcm_be_dai_shutdown(fe, stream);
+ if (err < 0)
+ dev_err(fe->dev,"dpcm: shutdown FE failed %d\n", err);
+
+ /* run the stream event for each BE */
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
+
+ return 0;
+}
+
+static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+ struct snd_soc_dpcm *dpcm;
+ enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+ int ret;
+
+ dev_dbg(fe->dev, "runtime %s open on FE %s\n",
+ stream ? "capture" : "playback", fe->dai_link->name);
+
+ /* Only start the BE if the FE is ready */
+ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
+ fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
+ return -EINVAL;
+
+ /* startup must always be called for new BEs */
+ ret = dpcm_be_dai_startup(fe, stream);
+ if (ret < 0) {
+ goto disconnect;
+ return ret;
+ }
+
+ /* keep going if FE state is > open */
+ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
+ return 0;
+
+ ret = dpcm_be_dai_hw_params(fe, stream);
+ if (ret < 0) {
+ goto close;
+ return ret;
+ }
+
+ /* keep going if FE state is > hw_params */
+ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
+ return 0;
+
+
+ ret = dpcm_be_dai_prepare(fe, stream);
+ if (ret < 0) {
+ goto hw_free;
+ return ret;
+ }
+
+ /* run the stream event for each BE */
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
+
+ /* keep going if FE state is > prepare */
+ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
+ fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
+ return 0;
+
+ if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
+ /* call trigger on the frontend - FE takes care of all BE triggers */
+ dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd start\n",
+ fe->dai_link->name);
+
+ ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: bespoke trigger FE failed %d\n", ret);
+ goto hw_free;
+ }
+ } else {
+ dev_dbg(fe->dev, "dpcm: trigger FE %s cmd start\n",
+ fe->dai_link->name);
+
+ ret = dpcm_be_dai_trigger(fe, stream,
+ SNDRV_PCM_TRIGGER_START);
+ if (ret < 0) {
+ dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+ goto hw_free;
+ }
+ }
+
+ return 0;
+
+hw_free:
+ dpcm_be_dai_hw_free(fe, stream);
+close:
+ dpcm_be_dai_shutdown(fe, stream);
+disconnect:
+ /* disconnect any non started BEs */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+ }
+
+ return ret;
+}
+
+static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ int ret;
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ ret = dpcm_run_update_startup(fe, stream);
+ if (ret < 0)
+ dev_err(fe->dev, "failed to startup some BEs\n");
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+ return ret;
+}
+
+static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ int ret;
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ ret = dpcm_run_update_shutdown(fe, stream);
+ if (ret < 0)
+ dev_err(fe->dev, "failed to shutdown some BEs\n");
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+ return ret;
+}
+
+/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
+ * any DAI links.
+ */
+int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
+{
+ struct snd_soc_card *card;
+ int i, old, new, paths;
+
+ if (widget->codec)
+ card = widget->codec->card;
+ else if (widget->platform)
+ card = widget->platform->card;
+ else
+ return -EINVAL;
+
+ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dapm_widget_list *list;
+ struct snd_soc_pcm_runtime *fe = &card->rtd[i];
+
+ /* make sure link is FE */
+ if (!fe->dai_link->dynamic)
+ continue;
+
+ /* only check active links */
+ if (!fe->cpu_dai->active)
+ continue;
+
+ /* DAPM sync will call this to update DSP paths */
+ dev_dbg(fe->dev, "DPCM runtime update for FE %s\n",
+ fe->dai_link->name);
+
+ /* skip if FE doesn't have playback capability */
+ if (!fe->cpu_dai->driver->playback.channels_min)
+ goto capture;
+
+ paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
+ if (paths < 0) {
+ dev_warn(fe->dev, "%s no valid %s path\n",
+ fe->dai_link->name, "playback");
+ mutex_unlock(&card->mutex);
+ return paths;
+ }
+
+ /* update any new playback paths */
+ new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
+ if (new) {
+ dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ /* update any old playback paths */
+ old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
+ if (old) {
+ dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+capture:
+ /* skip if FE doesn't have capture capability */
+ if (!fe->cpu_dai->driver->capture.channels_min)
+ continue;
+
+ paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
+ if (paths < 0) {
+ dev_warn(fe->dev, "%s no valid %s path\n",
+ fe->dai_link->name, "capture");
+ mutex_unlock(&card->mutex);
+ return paths;
+ }
+
+ /* update any new capture paths */
+ new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
+ if (new) {
+ dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
+ }
+
+ /* update any old capture paths */
+ old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
+ if (old) {
+ dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
+ dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
+ dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
+ }
+
+ dpcm_path_put(&list);
+ }
+
+ mutex_unlock(&card->mutex);
+ return 0;
+}
+int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
+{
+ struct snd_soc_dpcm *dpcm;
+ struct list_head *clients =
+ &fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients;
+
+ list_for_each_entry(dpcm, clients, list_be) {
+
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_soc_dai *dai = be->codec_dai;
+ struct snd_soc_dai_driver *drv = dai->driver;
+
+ if (be->dai_link->ignore_suspend)
+ continue;
+
+ dev_dbg(be->dev, "BE digital mute %s\n", be->dai_link->name);
+
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, mute);
+ }
+
+ return 0;
+}
+
+static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
+{
+ struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ struct snd_soc_dpcm *dpcm;
+ struct snd_soc_dapm_widget_list *list;
+ int ret;
+ int stream = fe_substream->stream;
+
+ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ fe->dpcm[stream].runtime = fe_substream->runtime;
+
+ if (dpcm_path_get(fe, stream, &list) <= 0) {
+ dev_warn(fe->dev, "asoc: %s no valid %s route\n",
+ fe->dai_link->name, stream ? "capture" : "playback");
+ mutex_unlock(&fe->card->mutex);
+ return -EINVAL;
+ }
+
+ /* calculate valid and active FE <-> BE dpcms */
+ dpcm_process_paths(fe, stream, &list, 1);
+
+ ret = dpcm_fe_dai_startup(fe_substream);
+ if (ret < 0) {
+ /* clean up all links */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+
+ dpcm_be_disconnect(fe, stream);
+ fe->dpcm[stream].runtime = NULL;
+ }
+
+ dpcm_clear_pending_state(fe, stream);
+ dpcm_path_put(&list);
+ mutex_unlock(&fe->card->mutex);
+ return ret;
+}
+
+static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
+{
+ struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ struct snd_soc_dpcm *dpcm;
+ int stream = fe_substream->stream, ret;
+
+ mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ ret = dpcm_fe_dai_shutdown(fe_substream);
+
+ /* mark FE's links ready to prune */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+ dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+
+ dpcm_be_disconnect(fe, stream);
+
+ fe->dpcm[stream].runtime = NULL;
+ mutex_unlock(&fe->card->mutex);
+ return ret;
+}
+
/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
@@ -641,56 +2001,94 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
- soc_pcm_ops->open = soc_pcm_open;
- soc_pcm_ops->close = soc_pcm_close;
- soc_pcm_ops->hw_params = soc_pcm_hw_params;
- soc_pcm_ops->hw_free = soc_pcm_hw_free;
- soc_pcm_ops->prepare = soc_pcm_prepare;
- soc_pcm_ops->trigger = soc_pcm_trigger;
- soc_pcm_ops->pointer = soc_pcm_pointer;
-
- /* check client and interface hw capabilities */
- snprintf(new_name, sizeof(new_name), "%s %s-%d",
- rtd->dai_link->stream_name, codec_dai->name, num);
-
- if (codec_dai->driver->playback.channels_min)
- playback = 1;
- if (codec_dai->driver->capture.channels_min)
- capture = 1;
-
- dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
- ret = snd_pcm_new(rtd->card->snd_card, new_name,
- num, playback, capture, &pcm);
+ if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
+ if (cpu_dai->driver->playback.channels_min)
+ playback = 1;
+ if (cpu_dai->driver->capture.channels_min)
+ capture = 1;
+ } else {
+ if (codec_dai->driver->playback.channels_min)
+ playback = 1;
+ if (codec_dai->driver->capture.channels_min)
+ capture = 1;
+ }
+
+ /* create the PCM */
+ if (rtd->dai_link->no_pcm) {
+ snprintf(new_name, sizeof(new_name), "(%s)",
+ rtd->dai_link->stream_name);
+
+ ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
+ playback, capture, &pcm);
+ } else {
+ if (rtd->dai_link->dynamic)
+ snprintf(new_name, sizeof(new_name), "%s (*)",
+ rtd->dai_link->stream_name);
+ else
+ snprintf(new_name, sizeof(new_name), "%s %s-%d",
+ rtd->dai_link->stream_name, codec_dai->name, num);
+
+ ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
+ capture, &pcm);
+ }
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
return ret;
}
+ dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name);
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
rtd->pcm = pcm;
pcm->private_data = rtd;
+
+ if (rtd->dai_link->no_pcm) {
+ if (playback)
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
+ if (capture)
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
+ goto out;
+ }
+
+ /* ASoC PCM operations */
+ if (rtd->dai_link->dynamic) {
+ rtd->ops.open = dpcm_fe_dai_open;
+ rtd->ops.hw_params = dpcm_fe_dai_hw_params;
+ rtd->ops.prepare = dpcm_fe_dai_prepare;
+ rtd->ops.trigger = dpcm_fe_dai_trigger;
+ rtd->ops.hw_free = dpcm_fe_dai_hw_free;
+ rtd->ops.close = dpcm_fe_dai_close;
+ rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.ioctl = soc_pcm_ioctl;
+ } else {
+ rtd->ops.open = soc_pcm_open;
+ rtd->ops.hw_params = soc_pcm_hw_params;
+ rtd->ops.prepare = soc_pcm_prepare;
+ rtd->ops.trigger = soc_pcm_trigger;
+ rtd->ops.hw_free = soc_pcm_hw_free;
+ rtd->ops.close = soc_pcm_close;
+ rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.ioctl = soc_pcm_ioctl;
+ }
+
if (platform->driver->ops) {
- soc_pcm_ops->mmap = platform->driver->ops->mmap;
- soc_pcm_ops->pointer = platform->driver->ops->pointer;
- soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops->copy = platform->driver->ops->copy;
- soc_pcm_ops->silence = platform->driver->ops->silence;
- soc_pcm_ops->ack = platform->driver->ops->ack;
- soc_pcm_ops->page = platform->driver->ops->page;
+ rtd->ops.ack = platform->driver->ops->ack;
+ rtd->ops.copy = platform->driver->ops->copy;
+ rtd->ops.silence = platform->driver->ops->silence;
+ rtd->ops.page = platform->driver->ops->page;
+ rtd->ops.mmap = platform->driver->ops->mmap;
}
if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd);
@@ -701,7 +2099,257 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
}
pcm->private_free = platform->driver->pcm_free;
+out:
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}
+
+/* is the current PCM operation for this FE ? */
+int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
+
+/* is the current PCM operation for this BE ? */
+int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
+ ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
+ be->dpcm[stream].runtime_update))
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
+
+/* get the substream for this BE */
+struct snd_pcm_substream *
+ snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
+{
+ return be->pcm->streams[stream].substream;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
+
+/* get the BE runtime state */
+enum snd_soc_dpcm_state
+ snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
+{
+ return be->dpcm[stream].state;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
+
+/* set the BE runtime state */
+void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
+ int stream, enum snd_soc_dpcm_state state)
+{
+ be->dpcm[stream].state = state;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
+
+/*
+ * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
+ * are not running, paused or suspended for the specified stream direction.
+ */
+int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ int state;
+
+ list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+
+ if (dpcm->fe == fe)
+ continue;
+
+ state = dpcm->fe->dpcm[stream].state;
+ if (state == SND_SOC_DPCM_STATE_START ||
+ state == SND_SOC_DPCM_STATE_PAUSED ||
+ state == SND_SOC_DPCM_STATE_SUSPEND)
+ return 0;
+ }
+
+ /* it's safe to free/stop this BE DAI */
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
+
+/*
+ * We can only change hw params a BE DAI if any of it's FE are not prepared,
+ * running, paused or suspended for the specified stream direction.
+ */
+int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ int state;
+
+ list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+
+ if (dpcm->fe == fe)
+ continue;
+
+ state = dpcm->fe->dpcm[stream].state;
+ if (state == SND_SOC_DPCM_STATE_START ||
+ state == SND_SOC_DPCM_STATE_PAUSED ||
+ state == SND_SOC_DPCM_STATE_SUSPEND ||
+ state == SND_SOC_DPCM_STATE_PREPARE)
+ return 0;
+ }
+
+ /* it's safe to change hw_params */
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
+
+int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_platform *platform)
+{
+ if (platform->driver->ops->trigger)
+ return platform->driver->ops->trigger(substream, cmd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_trigger);
+
+#ifdef CONFIG_DEBUG_FS
+static char *dpcm_state_string(enum snd_soc_dpcm_state state)
+{
+ switch (state) {
+ case SND_SOC_DPCM_STATE_NEW:
+ return "new";
+ case SND_SOC_DPCM_STATE_OPEN:
+ return "open";
+ case SND_SOC_DPCM_STATE_HW_PARAMS:
+ return "hw_params";
+ case SND_SOC_DPCM_STATE_PREPARE:
+ return "prepare";
+ case SND_SOC_DPCM_STATE_START:
+ return "start";
+ case SND_SOC_DPCM_STATE_STOP:
+ return "stop";
+ case SND_SOC_DPCM_STATE_SUSPEND:
+ return "suspend";
+ case SND_SOC_DPCM_STATE_PAUSED:
+ return "paused";
+ case SND_SOC_DPCM_STATE_HW_FREE:
+ return "hw_free";
+ case SND_SOC_DPCM_STATE_CLOSE:
+ return "close";
+ }
+
+ return "unknown";
+}
+
+static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+ int stream, char *buf, size_t size)
+{
+ struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
+ struct snd_soc_dpcm *dpcm;
+ ssize_t offset = 0;
+
+ /* FE state */
+ offset += snprintf(buf + offset, size - offset,
+ "[%s - %s]\n", fe->dai_link->name,
+ stream ? "Capture" : "Playback");
+
+ offset += snprintf(buf + offset, size - offset, "State: %s\n",
+ dpcm_state_string(fe->dpcm[stream].state));
+
+ if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+ offset += snprintf(buf + offset, size - offset,
+ "Hardware Params: "
+ "Format = %s, Channels = %d, Rate = %d\n",
+ snd_pcm_format_name(params_format(params)),
+ params_channels(params),
+ params_rate(params));
+
+ /* BEs state */
+ offset += snprintf(buf + offset, size - offset, "Backends:\n");
+
+ if (list_empty(&fe->dpcm[stream].be_clients)) {
+ offset += snprintf(buf + offset, size - offset,
+ " No active DSP links\n");
+ goto out;
+ }
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ params = &dpcm->hw_params;
+
+ offset += snprintf(buf + offset, size - offset,
+ "- %s\n", be->dai_link->name);
+
+ offset += snprintf(buf + offset, size - offset,
+ " State: %s\n",
+ dpcm_state_string(be->dpcm[stream].state));
+
+ if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+ offset += snprintf(buf + offset, size - offset,
+ " Hardware Params: "
+ "Format = %s, Channels = %d, Rate = %d\n",
+ snd_pcm_format_name(params_format(params)),
+ params_channels(params),
+ params_rate(params));
+ }
+
+out:
+ return offset;
+}
+
+static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_pcm_runtime *fe = file->private_data;
+ ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
+ char *buf;
+
+ buf = kmalloc(out_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (fe->cpu_dai->driver->playback.channels_min)
+ offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK,
+ buf + offset, out_count - offset);
+
+ if (fe->cpu_dai->driver->capture.channels_min)
+ offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE,
+ buf + offset, out_count - offset);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations dpcm_state_fops = {
+ .open = simple_open,
+ .read = dpcm_state_read_file,
+ .llseek = default_llseek,
+};
+
+int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
+{
+ if (!rtd->dai_link)
+ return 0;
+
+ rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
+ rtd->card->debugfs_card_root);
+ if (!rtd->debugfs_dpcm_root) {
+ dev_dbg(rtd->dev,
+ "ASoC: Failed to create dpcm debugfs directory %s\n",
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
+ rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444,
+ rtd->debugfs_dpcm_root,
+ rtd, &dpcm_state_fops);
+
+ return 0;
+}
+#endif
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index ce1b773c351..c1c8e955f4d 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,26 +1,63 @@
config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+ select REGMAP_MMIO
help
Say Y or M here if you want support for SoC audio on Tegra.
-config SND_SOC_TEGRA_I2S
+config SND_SOC_TEGRA20_DAS
tristate
- depends on SND_SOC_TEGRA
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+ help
+ Say Y or M if you want to add support for the Tegra20 DAS module.
+ You will also need to select the individual machine drivers to
+ support below.
+
+config SND_SOC_TEGRA20_I2S
+ tristate
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA20_DAS
help
Say Y or M if you want to add support for codecs attached to the
- Tegra I2S interface. You will also need to select the individual
+ Tegra20 I2S interface. You will also need to select the individual
machine drivers to support below.
-config SND_SOC_TEGRA_SPDIF
+config SND_SOC_TEGRA20_SPDIF
tristate
- depends on SND_SOC_TEGRA
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
default m
help
- Say Y or M if you want to add support for the SPDIF interface.
+ Say Y or M if you want to add support for the Tegra20 SPDIF interface.
You will also need to select the individual machine drivers to support
below.
+config SND_SOC_TEGRA30_AHUB
+ tristate
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
+ help
+ Say Y or M if you want to add support for the Tegra20 AHUB module.
+ You will also need to select the individual machine drivers to
+ support below.
+
+config SND_SOC_TEGRA30_I2S
+ tristate
+ depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
+ select SND_SOC_TEGRA30_AHUB
+ help
+ Say Y or M if you want to add support for codecs attached to the
+ Tegra30 I2S interface. You will also need to select the individual
+ machine drivers to support below.
+
+config SND_SOC_TEGRA_WM8753
+ tristate "SoC Audio support for Tegra boards using a WM8753 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_WM8753
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the WM8753 codec, such as Whistler.
+
config MACH_HAS_SND_SOC_TEGRA_WM8903
bool
help
@@ -32,7 +69,8 @@ config SND_SOC_TEGRA_WM8903
tristate "SoC Audio support for Tegra boards using a WM8903 codec"
depends on SND_SOC_TEGRA && I2C
depends on MACH_HAS_SND_SOC_TEGRA_WM8903
- select SND_SOC_TEGRA_I2S
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
select SND_SOC_WM8903
help
Say Y or M here if you want to add support for SoC audio on Tegra
@@ -42,17 +80,17 @@ config SND_SOC_TEGRA_WM8903
config SND_SOC_TEGRA_TRIMSLICE
tristate "SoC Audio support for TrimSlice board"
depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
- select SND_SOC_TEGRA_I2S
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
select SND_SOC_TLV320AIC23
help
Say Y or M here if you want to add support for SoC audio on the
TrimSlice platform.
config SND_SOC_TEGRA_ALC5632
- tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
- depends on SND_SOC_TEGRA && I2C
- select SND_SOC_TEGRA_I2S
- select SND_SOC_ALC5632
- help
- Say Y or M here if you want to add support for SoC audio on the
- Toshiba AC100 netbook.
+ tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
+ depends on SND_SOC_TEGRA && I2C
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_ALC5632
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ Toshiba AC100 netbook.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 8e584b8fcfb..391e78a34c0 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -1,21 +1,27 @@
# Tegra platform Support
-snd-soc-tegra-das-objs := tegra_das.o
snd-soc-tegra-pcm-objs := tegra_pcm.o
-snd-soc-tegra-i2s-objs := tegra_i2s.o
-snd-soc-tegra-spdif-objs := tegra_spdif.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+snd-soc-tegra20-das-objs := tegra20_das.o
+snd-soc-tegra20-i2s-objs := tegra20_i2s.o
+snd-soc-tegra20-spdif-objs := tegra20_spdif.o
+snd-soc-tegra30-ahub-objs := tegra30_ahub.o
+snd-soc-tegra30-i2s-objs := tegra30_i2s.o
-obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
-obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
-obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
-obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
+obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
+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-wm8753-objs := tegra_wm8753.o
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.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_TRIMSLICE) += snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
new file mode 100644
index 00000000000..bf99296bce9
--- /dev/null
+++ b/sound/soc/tegra/tegra20_das.c
@@ -0,0 +1,233 @@
+/*
+ * tegra20_das.c - Tegra20 DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include "tegra20_das.h"
+
+#define DRV_NAME "tegra20-das"
+
+static struct tegra20_das *das;
+
+static inline void tegra20_das_write(u32 reg, u32 val)
+{
+ regmap_write(das->regmap, reg, val);
+}
+
+static inline u32 tegra20_das_read(u32 reg)
+{
+ u32 val;
+ regmap_read(das->regmap, reg, &val);
+ return val;
+}
+
+int tegra20_das_connect_dap_to_dac(int dap, int dac)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA20_DAS_DAP_CTRL_SEL +
+ (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
+ reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
+
+ tegra20_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac);
+
+int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master,
+ int sdata1rx, int sdata2rx)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA20_DAS_DAP_CTRL_SEL +
+ (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
+ reg = otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
+ !!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
+ !!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
+ !!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
+
+ tegra20_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap);
+
+int tegra20_das_connect_dac_to_dap(int dac, int dap)
+{
+ u32 addr;
+ u32 reg;
+
+ if (!das)
+ return -ENODEV;
+
+ addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL +
+ (dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+ reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
+ dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
+ dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
+
+ tegra20_das_write(addr, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap);
+
+#define LAST_REG(name) \
+ (TEGRA20_DAS_##name + \
+ (TEGRA20_DAS_##name##_STRIDE * (TEGRA20_DAS_##name##_COUNT - 1)))
+
+static bool tegra20_das_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ if ((reg >= TEGRA20_DAS_DAP_CTRL_SEL) &&
+ (reg <= LAST_REG(DAP_CTRL_SEL)))
+ return true;
+ if ((reg >= TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) &&
+ (reg <= LAST_REG(DAC_INPUT_DATA_CLK_SEL)))
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config tegra20_das_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LAST_REG(DAC_INPUT_DATA_CLK_SEL),
+ .writeable_reg = tegra20_das_wr_rd_reg,
+ .readable_reg = tegra20_das_wr_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit tegra20_das_probe(struct platform_device *pdev)
+{
+ struct resource *res, *region;
+ void __iomem *regs;
+ int ret = 0;
+
+ if (das)
+ return -ENODEV;
+
+ das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL);
+ if (!das) {
+ dev_err(&pdev->dev, "Can't allocate tegra20_das\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ das->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ region = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name);
+ if (!region) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ das->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra20_das_regmap_config);
+ if (IS_ERR(das->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(das->regmap);
+ goto err;
+ }
+
+ ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1,
+ TEGRA20_DAS_DAP_SEL_DAC1);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
+ goto err;
+ }
+ ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1,
+ TEGRA20_DAS_DAC_SEL_DAP1);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, das);
+
+ return 0;
+
+err:
+ das = NULL;
+ return ret;
+}
+
+static int __devexit tegra20_das_remove(struct platform_device *pdev)
+{
+ if (!das)
+ return -ENODEV;
+
+ das = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id tegra20_das_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra20-das", },
+ {},
+};
+
+static struct platform_driver tegra20_das_driver = {
+ .probe = tegra20_das_probe,
+ .remove = __devexit_p(tegra20_das_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra20_das_of_match,
+ },
+};
+module_platform_driver(tegra20_das_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 DAS driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_das_of_match);
diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h
new file mode 100644
index 00000000000..be217f3d3a7
--- /dev/null
+++ b/sound/soc/tegra/tegra20_das.h
@@ -0,0 +1,134 @@
+/*
+ * tegra20_das.h - Definitions for Tegra20 DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_DAS_H__
+#define __TEGRA20_DAS_H__
+
+/* Register TEGRA20_DAS_DAP_CTRL_SEL */
+#define TEGRA20_DAS_DAP_CTRL_SEL 0x00
+#define TEGRA20_DAS_DAP_CTRL_SEL_COUNT 5
+#define TEGRA20_DAS_DAP_CTRL_SEL_STRIDE 4
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5
+
+/* Values for field TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
+#define TEGRA20_DAS_DAP_SEL_DAC1 0
+#define TEGRA20_DAS_DAP_SEL_DAC2 1
+#define TEGRA20_DAS_DAP_SEL_DAC3 2
+#define TEGRA20_DAS_DAP_SEL_DAP1 16
+#define TEGRA20_DAS_DAP_SEL_DAP2 17
+#define TEGRA20_DAS_DAP_SEL_DAP3 18
+#define TEGRA20_DAS_DAP_SEL_DAP4 19
+#define TEGRA20_DAS_DAP_SEL_DAP5 20
+
+/* Register TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL */
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL 0x40
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4
+
+/*
+ * Values for:
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
+ */
+#define TEGRA20_DAS_DAC_SEL_DAP1 0
+#define TEGRA20_DAS_DAC_SEL_DAP2 1
+#define TEGRA20_DAS_DAC_SEL_DAP3 2
+#define TEGRA20_DAS_DAC_SEL_DAP4 3
+#define TEGRA20_DAS_DAC_SEL_DAP5 4
+
+/*
+ * Names/IDs of the DACs/DAPs.
+ */
+
+#define TEGRA20_DAS_DAP_ID_1 0
+#define TEGRA20_DAS_DAP_ID_2 1
+#define TEGRA20_DAS_DAP_ID_3 2
+#define TEGRA20_DAS_DAP_ID_4 3
+#define TEGRA20_DAS_DAP_ID_5 4
+
+#define TEGRA20_DAS_DAC_ID_1 0
+#define TEGRA20_DAS_DAC_ID_2 1
+#define TEGRA20_DAS_DAC_ID_3 2
+
+struct tegra20_das {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+/*
+ * Terminology:
+ * DAS: Digital audio switch (HW module controlled by this driver)
+ * DAP: Digital audio port (port/pins on Tegra device)
+ * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
+ *
+ * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
+ * DAC, or another DAP. When DAPs are connected, one must be the master and
+ * one the slave. Each DAC allows selection of a specific DAP for input, to
+ * cater for the case where N DAPs are connected to 1 DAC for broadcast
+ * output.
+ *
+ * This driver is dumb; no attempt is made to ensure that a valid routing
+ * configuration is programmed.
+ */
+
+/*
+ * Connect a DAP to to a DAC
+ * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
+ * dac_sel: DAC to connect to: TEGRA20_DAS_DAP_SEL_DAC*
+ */
+extern int tegra20_das_connect_dap_to_dac(int dap_id, int dac_sel);
+
+/*
+ * Connect a DAP to to another DAP
+ * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
+ * other_dap_sel: DAP to connect to: TEGRA20_DAS_DAP_SEL_DAP*
+ * master: Is this DAP the master (1) or slave (0)
+ * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
+ * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
+ */
+extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
+ int master, int sdata1rx,
+ int sdata2rx);
+
+/*
+ * Connect a DAC's input to a DAP
+ * (DAC outputs are selected by the DAP)
+ * dac_id: DAC ID to connect: TEGRA20_DAS_DAC_ID_*
+ * dap_sel: DAP to receive input from: TEGRA20_DAS_DAC_SEL_DAP*
+ */
+extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel);
+
+#endif
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
new file mode 100644
index 00000000000..0c7af63d444
--- /dev/null
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -0,0 +1,494 @@
+/*
+ * tegra20_i2s.c - Tegra20 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra20_i2s.h"
+
+#define DRV_NAME "tegra20-i2s"
+
+static inline void tegra20_i2s_write(struct tegra20_i2s *i2s, u32 reg, u32 val)
+{
+ regmap_write(i2s->regmap, reg, val);
+}
+
+static inline u32 tegra20_i2s_read(struct tegra20_i2s *i2s, u32 reg)
+{
+ u32 val;
+ regmap_read(i2s->regmap, reg, &val);
+ return val;
+}
+
+static int tegra20_i2s_runtime_suspend(struct device *dev)
+{
+ struct tegra20_i2s *i2s = dev_get_drvdata(dev);
+
+ clk_disable(i2s->clk_i2s);
+
+ return 0;
+}
+
+static int tegra20_i2s_runtime_resume(struct device *dev)
+{
+ struct tegra20_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(i2s->clk_i2s);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_MASTER_ENABLE;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~(TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
+ TEGRA20_I2S_CTRL_LRCK_MASK);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_R_LOW;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S;
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM;
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM;
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 reg;
+ int ret, sample_size, srate, i2sclock, bitcnt;
+
+ i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_BIT_SIZE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_16;
+ sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_24;
+ sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_32;
+ sample_size = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ srate = params_rate(params);
+
+ /* Final "* 2" required by Tegra hardware */
+ i2sclock = srate * params_channels(params) * sample_size * 2;
+
+ ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+ return -EINVAL;
+ reg = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (i2sclock % (2 * srate))
+ reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
+
+ tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg);
+
+ tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR,
+ TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
+ TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+
+ return 0;
+}
+
+static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s)
+{
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO1_ENABLE;
+ tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s)
+{
+ i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO1_ENABLE;
+ tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s)
+{
+ i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO2_ENABLE;
+ tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s)
+{
+ i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO2_ENABLE;
+ tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra20_i2s_start_playback(i2s);
+ else
+ tegra20_i2s_start_capture(i2s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra20_i2s_stop_playback(i2s);
+ else
+ tegra20_i2s_stop_capture(i2s);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra20_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = &i2s->capture_dma_data;
+ dai->playback_dma_data = &i2s->playback_dma_data;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
+ .set_fmt = tegra20_i2s_set_fmt,
+ .hw_params = tegra20_i2s_hw_params,
+ .trigger = tegra20_i2s_trigger,
+};
+
+static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
+ .probe = tegra20_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra20_i2s_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_I2S_CTRL:
+ case TEGRA20_I2S_STATUS:
+ case TEGRA20_I2S_TIMING:
+ case TEGRA20_I2S_FIFO_SCR:
+ case TEGRA20_I2S_PCM_CTRL:
+ case TEGRA20_I2S_NW_CTRL:
+ case TEGRA20_I2S_TDM_CTRL:
+ case TEGRA20_I2S_TDM_TX_RX_CTRL:
+ case TEGRA20_I2S_FIFO1:
+ case TEGRA20_I2S_FIFO2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_I2S_STATUS:
+ case TEGRA20_I2S_FIFO_SCR:
+ case TEGRA20_I2S_FIFO1:
+ case TEGRA20_I2S_FIFO2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_I2S_FIFO1:
+ case TEGRA20_I2S_FIFO2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra20_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA20_I2S_FIFO2,
+ .writeable_reg = tegra20_i2s_wr_rd_reg,
+ .readable_reg = tegra20_i2s_wr_rd_reg,
+ .volatile_reg = tegra20_i2s_volatile_reg,
+ .precious_reg = tegra20_i2s_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev)
+{
+ struct tegra20_i2s *i2s;
+ struct resource *mem, *memregion, *dmareq;
+ u32 of_dma[2];
+ u32 dma_ch;
+ void __iomem *regs;
+ int ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
+ if (!i2s) {
+ dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ i2s->dai = tegra20_i2s_dai_template;
+ i2s->dai.name = dev_name(&pdev->dev);
+
+ i2s->clk_i2s = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2s->clk_i2s)) {
+ dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmareq) {
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "nvidia,dma-request-selector",
+ of_dma, 2) < 0) {
+ dev_err(&pdev->dev, "No DMA resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+ dma_ch = of_dma[1];
+ } else {
+ dma_ch = dmareq->start;
+ }
+
+ 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;
+ goto err_clk_put;
+ }
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra20_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(i2s->regmap);
+ goto err_clk_put;
+ }
+
+ i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
+ i2s->capture_dma_data.wrap = 4;
+ i2s->capture_dma_data.width = 32;
+ i2s->capture_dma_data.req_sel = dma_ch;
+
+ i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
+ i2s->playback_dma_data.wrap = 4;
+ i2s->playback_dma_data.width = 32;
+ i2s->playback_dma_data.req_sel = dma_ch;
+
+ i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra20_i2s_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_suspend;
+ }
+
+ ret = tegra_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_dai;
+ }
+
+ return 0;
+
+err_unregister_dai:
+ snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra20_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ clk_put(i2s->clk_i2s);
+err:
+ return ret;
+}
+
+static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev)
+{
+ struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra20_i2s_runtime_suspend(&pdev->dev);
+
+ tegra_pcm_platform_unregister(&pdev->dev);
+ snd_soc_unregister_dai(&pdev->dev);
+
+ clk_put(i2s->clk_i2s);
+
+ return 0;
+}
+
+static const struct of_device_id tegra20_i2s_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra20-i2s", },
+ {},
+};
+
+static const struct dev_pm_ops tegra20_i2s_pm_ops __devinitconst = {
+ SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
+ tegra20_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra20_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra20_i2s_of_match,
+ .pm = &tegra20_i2s_pm_ops,
+ },
+ .probe = tegra20_i2s_platform_probe,
+ .remove = __devexit_p(tegra20_i2s_platform_remove),
+};
+module_platform_driver(tegra20_i2s_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match);
diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h
new file mode 100644
index 00000000000..a57efc6a597
--- /dev/null
+++ b/sound/soc/tegra/tegra20_i2s.h
@@ -0,0 +1,164 @@
+/*
+ * tegra20_i2s.h - Definitions for Tegra20 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_I2S_H__
+#define __TEGRA20_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA20_I2S1_BASE and TEGRA20_I2S2_BASE */
+
+#define TEGRA20_I2S_CTRL 0x00
+#define TEGRA20_I2S_STATUS 0x04
+#define TEGRA20_I2S_TIMING 0x08
+#define TEGRA20_I2S_FIFO_SCR 0x0c
+#define TEGRA20_I2S_PCM_CTRL 0x10
+#define TEGRA20_I2S_NW_CTRL 0x14
+#define TEGRA20_I2S_TDM_CTRL 0x20
+#define TEGRA20_I2S_TDM_TX_RX_CTRL 0x24
+#define TEGRA20_I2S_FIFO1 0x40
+#define TEGRA20_I2S_FIFO2 0x80
+
+/* Fields in TEGRA20_I2S_CTRL */
+
+#define TEGRA20_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30)
+#define TEGRA20_I2S_CTRL_FIFO1_ENABLE (1 << 29)
+#define TEGRA20_I2S_CTRL_FIFO2_ENABLE (1 << 28)
+#define TEGRA20_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27)
+#define TEGRA20_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26)
+#define TEGRA20_I2S_CTRL_MASTER_ENABLE (1 << 25)
+
+#define TEGRA20_I2S_LRCK_LEFT_LOW 0
+#define TEGRA20_I2S_LRCK_RIGHT_LOW 1
+
+#define TEGRA20_I2S_CTRL_LRCK_SHIFT 24
+#define TEGRA20_I2S_CTRL_LRCK_MASK (1 << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA20_I2S_CTRL_LRCK_L_LOW (TEGRA20_I2S_LRCK_LEFT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA20_I2S_CTRL_LRCK_R_LOW (TEGRA20_I2S_LRCK_RIGHT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA20_I2S_BIT_FORMAT_I2S 0
+#define TEGRA20_I2S_BIT_FORMAT_RJM 1
+#define TEGRA20_I2S_BIT_FORMAT_LJM 2
+#define TEGRA20_I2S_BIT_FORMAT_DSP 3
+
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT 10
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_I2S (TEGRA20_I2S_BIT_FORMAT_I2S << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_RJM (TEGRA20_I2S_BIT_FORMAT_RJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_LJM (TEGRA20_I2S_BIT_FORMAT_LJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_DSP (TEGRA20_I2S_BIT_FORMAT_DSP << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+
+#define TEGRA20_I2S_BIT_SIZE_16 0
+#define TEGRA20_I2S_BIT_SIZE_20 1
+#define TEGRA20_I2S_BIT_SIZE_24 2
+#define TEGRA20_I2S_BIT_SIZE_32 3
+
+#define TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT 8
+#define TEGRA20_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_16 (TEGRA20_I2S_BIT_SIZE_16 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_20 (TEGRA20_I2S_BIT_SIZE_20 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_24 (TEGRA20_I2S_BIT_SIZE_24 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_32 (TEGRA20_I2S_BIT_SIZE_32 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+
+#define TEGRA20_I2S_FIFO_16_LSB 0
+#define TEGRA20_I2S_FIFO_20_LSB 1
+#define TEGRA20_I2S_FIFO_24_LSB 2
+#define TEGRA20_I2S_FIFO_32 3
+#define TEGRA20_I2S_FIFO_PACKED 7
+
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT 4
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA20_I2S_FIFO_16_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA20_I2S_FIFO_20_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA20_I2S_FIFO_24_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_32 (TEGRA20_I2S_FIFO_32 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA20_I2S_FIFO_PACKED << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+
+#define TEGRA20_I2S_CTRL_IE_FIFO1_ERR (1 << 3)
+#define TEGRA20_I2S_CTRL_IE_FIFO2_ERR (1 << 2)
+#define TEGRA20_I2S_CTRL_QE_FIFO1 (1 << 1)
+#define TEGRA20_I2S_CTRL_QE_FIFO2 (1 << 0)
+
+/* Fields in TEGRA20_I2S_STATUS */
+
+#define TEGRA20_I2S_STATUS_FIFO1_RDY (1 << 31)
+#define TEGRA20_I2S_STATUS_FIFO2_RDY (1 << 30)
+#define TEGRA20_I2S_STATUS_FIFO1_BSY (1 << 29)
+#define TEGRA20_I2S_STATUS_FIFO2_BSY (1 << 28)
+#define TEGRA20_I2S_STATUS_FIFO1_ERR (1 << 3)
+#define TEGRA20_I2S_STATUS_FIFO2_ERR (1 << 2)
+#define TEGRA20_I2S_STATUS_QS_FIFO1 (1 << 1)
+#define TEGRA20_I2S_STATUS_QS_FIFO2 (1 << 0)
+
+/* Fields in TEGRA20_I2S_TIMING */
+
+#define TEGRA20_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA20_I2S_FIFO_SCR */
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
+#define TEGRA20_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_CLR (1 << 12)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_CLR (1 << 8)
+
+#define TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT 0
+#define TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
+#define TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
+#define TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+
+struct tegra20_i2s {
+ struct snd_soc_dai_driver dai;
+ struct clk *clk_i2s;
+ struct tegra_pcm_dma_params capture_dma_data;
+ struct tegra_pcm_dma_params playback_dma_data;
+ struct regmap *regmap;
+ u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
new file mode 100644
index 00000000000..f9b57418bd0
--- /dev/null
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -0,0 +1,404 @@
+/*
+ * tegra20_spdif.c - Tegra20 SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2011-2012 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra20_spdif.h"
+
+#define DRV_NAME "tegra20-spdif"
+
+static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg,
+ u32 val)
+{
+ regmap_write(spdif->regmap, reg, val);
+}
+
+static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg)
+{
+ u32 val;
+ regmap_read(spdif->regmap, reg, &val);
+ return val;
+}
+
+static int tegra20_spdif_runtime_suspend(struct device *dev)
+{
+ struct tegra20_spdif *spdif = dev_get_drvdata(dev);
+
+ clk_disable(spdif->clk_spdif_out);
+
+ return 0;
+}
+
+static int tegra20_spdif_runtime_resume(struct device *dev)
+{
+ struct tegra20_spdif *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(spdif->clk_spdif_out);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret, spdifclock;
+
+ spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK;
+ spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK;
+ spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 32000:
+ spdifclock = 4096000;
+ break;
+ case 44100:
+ spdifclock = 5644800;
+ break;
+ case 48000:
+ spdifclock = 6144000;
+ break;
+ case 88200:
+ spdifclock = 11289600;
+ break;
+ case 96000:
+ spdifclock = 12288000;
+ break;
+ case 176400:
+ spdifclock = 22579200;
+ break;
+ case 192000:
+ spdifclock = 24576000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
+ if (ret) {
+ dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
+{
+ spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN;
+ tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
+{
+ spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN;
+ tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ tegra20_spdif_start_playback(spdif);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra20_spdif_stop_playback(spdif);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra20_spdif_probe(struct snd_soc_dai *dai)
+{
+ struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = NULL;
+ dai->playback_dma_data = &spdif->playback_dma_data;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
+ .hw_params = tegra20_spdif_hw_params,
+ .trigger = tegra20_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver tegra20_spdif_dai = {
+ .name = DRV_NAME,
+ .probe = tegra20_spdif_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra20_spdif_dai_ops,
+};
+
+static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_SPDIF_CTRL:
+ case TEGRA20_SPDIF_STATUS:
+ case TEGRA20_SPDIF_STROBE_CTRL:
+ case TEGRA20_SPDIF_DATA_FIFO_CSR:
+ case TEGRA20_SPDIF_DATA_OUT:
+ case TEGRA20_SPDIF_DATA_IN:
+ case TEGRA20_SPDIF_CH_STA_RX_A:
+ case TEGRA20_SPDIF_CH_STA_RX_B:
+ case TEGRA20_SPDIF_CH_STA_RX_C:
+ case TEGRA20_SPDIF_CH_STA_RX_D:
+ case TEGRA20_SPDIF_CH_STA_RX_E:
+ case TEGRA20_SPDIF_CH_STA_RX_F:
+ case TEGRA20_SPDIF_CH_STA_TX_A:
+ case TEGRA20_SPDIF_CH_STA_TX_B:
+ case TEGRA20_SPDIF_CH_STA_TX_C:
+ case TEGRA20_SPDIF_CH_STA_TX_D:
+ case TEGRA20_SPDIF_CH_STA_TX_E:
+ case TEGRA20_SPDIF_CH_STA_TX_F:
+ case TEGRA20_SPDIF_USR_STA_RX_A:
+ case TEGRA20_SPDIF_USR_DAT_TX_A:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_SPDIF_STATUS:
+ case TEGRA20_SPDIF_DATA_FIFO_CSR:
+ case TEGRA20_SPDIF_DATA_OUT:
+ case TEGRA20_SPDIF_DATA_IN:
+ case TEGRA20_SPDIF_CH_STA_RX_A:
+ case TEGRA20_SPDIF_CH_STA_RX_B:
+ case TEGRA20_SPDIF_CH_STA_RX_C:
+ case TEGRA20_SPDIF_CH_STA_RX_D:
+ case TEGRA20_SPDIF_CH_STA_RX_E:
+ case TEGRA20_SPDIF_CH_STA_RX_F:
+ case TEGRA20_SPDIF_USR_STA_RX_A:
+ case TEGRA20_SPDIF_USR_DAT_TX_A:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA20_SPDIF_DATA_OUT:
+ case TEGRA20_SPDIF_DATA_IN:
+ case TEGRA20_SPDIF_USR_STA_RX_A:
+ case TEGRA20_SPDIF_USR_DAT_TX_A:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra20_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
+ .writeable_reg = tegra20_spdif_wr_rd_reg,
+ .readable_reg = tegra20_spdif_wr_rd_reg,
+ .volatile_reg = tegra20_spdif_volatile_reg,
+ .precious_reg = tegra20_spdif_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
+{
+ struct tegra20_spdif *spdif;
+ struct resource *mem, *memregion, *dmareq;
+ void __iomem *regs;
+ int ret;
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
+ GFP_KERNEL);
+ if (!spdif) {
+ dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
+ if (IS_ERR(spdif->clk_spdif_out)) {
+ pr_err("Can't retrieve spdif clock\n");
+ ret = PTR_ERR(spdif->clk_spdif_out);
+ goto err;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmareq) {
+ dev_err(&pdev->dev, "No DMA resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ memregion = 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;
+ goto err_clk_put;
+ }
+
+ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra20_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(spdif->regmap);
+ goto err_clk_put;
+ }
+
+ spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
+ spdif->playback_dma_data.wrap = 4;
+ spdif->playback_dma_data.width = 32;
+ spdif->playback_dma_data.req_sel = dmareq->start;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra20_spdif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_suspend;
+ }
+
+ ret = tegra_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_dai;
+ }
+
+ return 0;
+
+err_unregister_dai:
+ snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra20_spdif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ clk_put(spdif->clk_spdif_out);
+err:
+ return ret;
+}
+
+static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
+{
+ struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra20_spdif_runtime_suspend(&pdev->dev);
+
+ tegra_pcm_platform_unregister(&pdev->dev);
+ snd_soc_unregister_dai(&pdev->dev);
+
+ clk_put(spdif->clk_spdif_out);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
+ SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
+ tegra20_spdif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra20_spdif_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &tegra20_spdif_pm_ops,
+ },
+ .probe = tegra20_spdif_platform_probe,
+ .remove = __devexit_p(tegra20_spdif_platform_remove),
+};
+
+module_platform_driver(tegra20_spdif_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h
new file mode 100644
index 00000000000..ed756527efe
--- /dev/null
+++ b/sound/soc/tegra/tegra20_spdif.h
@@ -0,0 +1,471 @@
+/*
+ * tegra20_spdif.h - Definitions for Tegra20 SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ * Copyright (c) 2008-2009, NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_SPDIF_H__
+#define __TEGRA20_SPDIF_H__
+
+#include "tegra_pcm.h"
+
+/* Offsets from TEGRA20_SPDIF_BASE */
+
+#define TEGRA20_SPDIF_CTRL 0x0
+#define TEGRA20_SPDIF_STATUS 0x4
+#define TEGRA20_SPDIF_STROBE_CTRL 0x8
+#define TEGRA20_SPDIF_DATA_FIFO_CSR 0x0C
+#define TEGRA20_SPDIF_DATA_OUT 0x40
+#define TEGRA20_SPDIF_DATA_IN 0x80
+#define TEGRA20_SPDIF_CH_STA_RX_A 0x100
+#define TEGRA20_SPDIF_CH_STA_RX_B 0x104
+#define TEGRA20_SPDIF_CH_STA_RX_C 0x108
+#define TEGRA20_SPDIF_CH_STA_RX_D 0x10C
+#define TEGRA20_SPDIF_CH_STA_RX_E 0x110
+#define TEGRA20_SPDIF_CH_STA_RX_F 0x114
+#define TEGRA20_SPDIF_CH_STA_TX_A 0x140
+#define TEGRA20_SPDIF_CH_STA_TX_B 0x144
+#define TEGRA20_SPDIF_CH_STA_TX_C 0x148
+#define TEGRA20_SPDIF_CH_STA_TX_D 0x14C
+#define TEGRA20_SPDIF_CH_STA_TX_E 0x150
+#define TEGRA20_SPDIF_CH_STA_TX_F 0x154
+#define TEGRA20_SPDIF_USR_STA_RX_A 0x180
+#define TEGRA20_SPDIF_USR_DAT_TX_A 0x1C0
+
+/* Fields in TEGRA20_SPDIF_CTRL */
+
+/* Start capturing from 0=right, 1=left channel */
+#define TEGRA20_SPDIF_CTRL_CAP_LC (1 << 30)
+
+/* SPDIF receiver(RX) enable */
+#define TEGRA20_SPDIF_CTRL_RX_EN (1 << 29)
+
+/* SPDIF Transmitter(TX) enable */
+#define TEGRA20_SPDIF_CTRL_TX_EN (1 << 28)
+
+/* Transmit Channel status */
+#define TEGRA20_SPDIF_CTRL_TC_EN (1 << 27)
+
+/* Transmit user Data */
+#define TEGRA20_SPDIF_CTRL_TU_EN (1 << 26)
+
+/* Interrupt on transmit error */
+#define TEGRA20_SPDIF_CTRL_IE_TXE (1 << 25)
+
+/* Interrupt on receive error */
+#define TEGRA20_SPDIF_CTRL_IE_RXE (1 << 24)
+
+/* Interrupt on invalid preamble */
+#define TEGRA20_SPDIF_CTRL_IE_P (1 << 23)
+
+/* Interrupt on "B" preamble */
+#define TEGRA20_SPDIF_CTRL_IE_B (1 << 22)
+
+/* Interrupt when block of channel status received */
+#define TEGRA20_SPDIF_CTRL_IE_C (1 << 21)
+
+/* Interrupt when a valid information unit (IU) is received */
+#define TEGRA20_SPDIF_CTRL_IE_U (1 << 20)
+
+/* Interrupt when RX user FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_RU (1 << 19)
+
+/* Interrupt when TX user FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_TU (1 << 18)
+
+/* Interrupt when RX data FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_RX (1 << 17)
+
+/* Interrupt when TX data FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_TX (1 << 16)
+
+/* Loopback test mode enable */
+#define TEGRA20_SPDIF_CTRL_LBK_EN (1 << 15)
+
+/*
+ * Pack data mode:
+ * 0 = Single data (16 bit needs to be padded to match the
+ * interface data bit size).
+ * 1 = Packeted left/right channel data into a single word.
+ */
+#define TEGRA20_SPDIF_CTRL_PACK (1 << 14)
+
+/*
+ * 00 = 16bit data
+ * 01 = 20bit data
+ * 10 = 24bit data
+ * 11 = raw data
+ */
+#define TEGRA20_SPDIF_BIT_MODE_16BIT 0
+#define TEGRA20_SPDIF_BIT_MODE_20BIT 1
+#define TEGRA20_SPDIF_BIT_MODE_24BIT 2
+#define TEGRA20_SPDIF_BIT_MODE_RAW 3
+
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT 12
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA20_SPDIF_BIT_MODE_16BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA20_SPDIF_BIT_MODE_20BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA20_SPDIF_BIT_MODE_24BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_RAW (TEGRA20_SPDIF_BIT_MODE_RAW << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_STATUS */
+
+/*
+ * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
+ * write a 1 to the corresponding bit location to clear the status.
+ */
+
+/*
+ * Receiver(RX) shifter is busy receiving data.
+ * This bit is asserted when the receiver first locked onto the
+ * preamble of the data stream after RX_EN is asserted. This bit is
+ * deasserted when either,
+ * (a) the end of a frame is reached after RX_EN is deeasserted, or
+ * (b) the SPDIF data stream becomes inactive.
+ */
+#define TEGRA20_SPDIF_STATUS_RX_BSY (1 << 29)
+
+/*
+ * Transmitter(TX) shifter is busy transmitting data.
+ * This bit is asserted when TX_EN is asserted.
+ * This bit is deasserted when the end of a frame is reached after
+ * TX_EN is deasserted.
+ */
+#define TEGRA20_SPDIF_STATUS_TX_BSY (1 << 28)
+
+/*
+ * TX is busy shifting out channel status.
+ * This bit is asserted when both TX_EN and TC_EN are asserted and
+ * data from CH_STA_TX_A register is loaded into the internal shifter.
+ * This bit is deasserted when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) CH_STA_TX_F register is loaded into the internal shifter.
+ */
+#define TEGRA20_SPDIF_STATUS_TC_BSY (1 << 27)
+
+/*
+ * TX User data FIFO busy.
+ * This bit is asserted when TX_EN and TXU_EN are asserted and
+ * there's data in the TX user FIFO. This bit is deassert when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) there's no data left in the TX user FIFO.
+ */
+#define TEGRA20_SPDIF_STATUS_TU_BSY (1 << 26)
+
+/* TX FIFO Underrun error status */
+#define TEGRA20_SPDIF_STATUS_TX_ERR (1 << 25)
+
+/* RX FIFO Overrun error status */
+#define TEGRA20_SPDIF_STATUS_RX_ERR (1 << 24)
+
+/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
+#define TEGRA20_SPDIF_STATUS_IS_P (1 << 23)
+
+/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
+#define TEGRA20_SPDIF_STATUS_IS_B (1 << 22)
+
+/*
+ * RX channel block data receive status:
+ * 0=entire block not recieved yet.
+ * 1=received entire block of channel status,
+ */
+#define TEGRA20_SPDIF_STATUS_IS_C (1 << 21)
+
+/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
+#define TEGRA20_SPDIF_STATUS_IS_U (1 << 20)
+
+/*
+ * RX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_RU (1 << 19)
+
+/*
+ * TX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_TU (1 << 18)
+
+/*
+ * RX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_RX (1 << 17)
+
+/*
+ * TX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_TX (1 << 16)
+
+/* Fields in TEGRA20_SPDIF_STROBE_CTRL */
+
+/*
+ * Indicates the approximate number of detected SPDIFIN clocks within a
+ * bi-phase period.
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16
+#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
+
+/* Data strobe mode: 0=Auto-locked 1=Manual locked */
+#define TEGRA20_SPDIF_STROBE_CTRL_STROBE (1 << 15)
+
+/*
+ * Manual data strobe time within the bi-phase clock period (in terms of
+ * the number of over-sampling clocks).
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8
+#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
+
+/*
+ * Manual SPDIFIN bi-phase clock period (in terms of the number of
+ * over-sampling clocks).
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0
+#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
+
+/* Fields in SPDIF_DATA_FIFO_CSR */
+
+/* Clear Receiver User FIFO (RX USR.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31)
+
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3
+
+/* RU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \
+ (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+
+/* Number of RX USR.FIFO levels with valid data. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter User FIFO (TX USR.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23)
+
+/* TU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \
+ (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+
+/* Number of TX USR.FIFO levels that could be filled. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
+
+/* Clear Receiver Data FIFO (RX DATA.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15)
+
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3
+
+/* RU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \
+ (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+
+/* Number of RX DATA.FIFO levels with valid data. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7)
+
+/* TU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \
+ (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \
+ (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+
+/* Number of TX DATA.FIFO levels that could be filled. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_DATA_OUT */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit (BIT_MODE=00, PACK=0)
+ * 20-bit (BIT_MODE=01, PACK=0)
+ * 24-bit (BIT_MODE=10, PACK=0)
+ * raw (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ */
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT 0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT 0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT 0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_DATA_IN */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit (BIT_MODE=00, PACK=0)
+ * 20-bit (BIT_MODE=01, PACK=0)
+ * 24-bit (BIT_MODE=10, PACK=0)
+ * raw (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ *
+ * Bits 31:24 are common to all modes except 16-bit packed
+ */
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_P (1 << 31)
+#define TEGRA20_SPDIF_DATA_IN_DATA_C (1 << 30)
+#define TEGRA20_SPDIF_DATA_IN_DATA_U (1 << 29)
+#define TEGRA20_SPDIF_DATA_IN_DATA_V (1 << 28)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24
+#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT 0
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT 0
+#define TEGRA20_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT 0
+#define TEGRA20_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_A */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_B */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_C */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_D */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_E */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_F */
+
+/*
+ * The 6-word receive channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of receive is from LSB to MSB
+ * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
+ */
+
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_A */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_B */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_C */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_D */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_E */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_F */
+
+/*
+ * The 6-word transmit channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of transmission is from LSB to MSB
+ * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
+ */
+
+/* Fields in TEGRA20_SPDIF_USR_STA_RX_A */
+
+/*
+ * This 4-word deep FIFO receives user FIFO field information. The order of
+ * receive is from LSB to MSB bit.
+ */
+
+/* Fields in TEGRA20_SPDIF_USR_DAT_TX_A */
+
+/*
+ * This 4-word deep FIFO transmits user FIFO field information. The order of
+ * transmission is from LSB to MSB bit.
+ */
+
+struct tegra20_spdif {
+ struct clk *clk_spdif_out;
+ struct tegra_pcm_dma_params capture_dma_data;
+ struct tegra_pcm_dma_params playback_dma_data;
+ struct regmap *regmap;
+ u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
new file mode 100644
index 00000000000..57cd419f743
--- /dev/null
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -0,0 +1,631 @@
+/*
+ * tegra30_ahub.c - Tegra30 AHUB driver
+ *
+ * Copyright (c) 2011,2012, 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/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <mach/clk.h>
+#include <mach/dma.h>
+#include <sound/soc.h>
+#include "tegra30_ahub.h"
+
+#define DRV_NAME "tegra30-ahub"
+
+static struct tegra30_ahub *ahub;
+
+static inline void tegra30_apbif_write(u32 reg, u32 val)
+{
+ regmap_write(ahub->regmap_apbif, reg, val);
+}
+
+static inline u32 tegra30_apbif_read(u32 reg)
+{
+ u32 val;
+ regmap_read(ahub->regmap_apbif, reg, &val);
+ return val;
+}
+
+static inline void tegra30_audio_write(u32 reg, u32 val)
+{
+ regmap_write(ahub->regmap_ahub, reg, val);
+}
+
+static int tegra30_ahub_runtime_suspend(struct device *dev)
+{
+ regcache_cache_only(ahub->regmap_apbif, true);
+ regcache_cache_only(ahub->regmap_ahub, true);
+
+ clk_disable(ahub->clk_apbif);
+ clk_disable(ahub->clk_d_audio);
+
+ return 0;
+}
+
+/*
+ * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data
+ * is read from or sent to memory. However, that's not something the rest of
+ * the driver supports right now, so we'll just treat the two clocks as one
+ * for now.
+ *
+ * These functions should not be a plain ref-count. Instead, each active stream
+ * contributes some requirement to the minimum clock rate, so starting or
+ * stopping streams should dynamically adjust the clock as required. However,
+ * this is not yet implemented.
+ */
+static int tegra30_ahub_runtime_resume(struct device *dev)
+{
+ int ret;
+
+ ret = clk_enable(ahub->clk_d_audio);
+ if (ret) {
+ dev_err(dev, "clk_enable d_audio failed: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(ahub->clk_apbif);
+ if (ret) {
+ dev_err(dev, "clk_enable apbif failed: %d\n", ret);
+ clk_disable(ahub->clk_d_audio);
+ return ret;
+ }
+
+ regcache_cache_only(ahub->regmap_apbif, false);
+ regcache_cache_only(ahub->regmap_ahub, false);
+
+ return 0;
+}
+
+int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
+ unsigned long *fiforeg,
+ unsigned long *reqsel)
+{
+ int channel;
+ u32 reg, val;
+
+ channel = find_first_zero_bit(ahub->rx_usage,
+ TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+ if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
+ return -EBUSY;
+
+ __set_bit(channel, ahub->rx_usage);
+
+ *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel;
+ *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
+ (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE);
+ *reqsel = ahub->dma_sel + channel;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK |
+ TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK);
+ val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) |
+ TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+ TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16;
+ tegra30_apbif_write(reg, val);
+
+ reg = TEGRA30_AHUB_CIF_RX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo);
+
+int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo);
+
+int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo);
+
+int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+
+ __clear_bit(channel, ahub->rx_usage);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo);
+
+int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
+ unsigned long *fiforeg,
+ unsigned long *reqsel)
+{
+ int channel;
+ u32 reg, val;
+
+ channel = find_first_zero_bit(ahub->tx_usage,
+ TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+ if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
+ return -EBUSY;
+
+ __set_bit(channel, ahub->tx_usage);
+
+ *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel;
+ *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
+ (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE);
+ *reqsel = ahub->dma_sel + channel;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK |
+ TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK);
+ val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) |
+ TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+ TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16;
+ tegra30_apbif_write(reg, val);
+
+ reg = TEGRA30_AHUB_CIF_TX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo);
+
+int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo);
+
+int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
+ tegra30_apbif_write(reg, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo);
+
+int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+
+ __clear_bit(channel, ahub->tx_usage);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo);
+
+int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
+ enum tegra30_ahub_txcif txcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg;
+
+ reg = TEGRA30_AHUB_AUDIO_RX +
+ (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
+ tegra30_audio_write(reg, 1 << txcif);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source);
+
+int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg;
+
+ reg = TEGRA30_AHUB_AUDIO_RX +
+ (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
+ tegra30_audio_write(reg, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source);
+
+static const char * const configlink_clocks[] __devinitconst = {
+ "i2s0",
+ "i2s1",
+ "i2s2",
+ "i2s3",
+ "i2s4",
+ "dam0",
+ "dam1",
+ "dam2",
+ "spdif_in",
+};
+
+struct of_dev_auxdata ahub_auxdata[] __devinitdata = {
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL),
+ {}
+};
+
+#define LAST_REG(name) \
+ (TEGRA30_AHUB_##name + \
+ (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4)
+
+#define REG_IN_ARRAY(reg, name) \
+ ((reg >= TEGRA30_AHUB_##name) && \
+ (reg <= LAST_REG(name) && \
+ (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE))))
+
+static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_AHUB_CONFIG_LINK_CTRL:
+ case TEGRA30_AHUB_MISC_CTRL:
+ case TEGRA30_AHUB_APBDMA_LIVE_STATUS:
+ case TEGRA30_AHUB_I2S_LIVE_STATUS:
+ case TEGRA30_AHUB_SPDIF_LIVE_STATUS:
+ case TEGRA30_AHUB_I2S_INT_MASK:
+ case TEGRA30_AHUB_DAM_INT_MASK:
+ case TEGRA30_AHUB_SPDIF_INT_MASK:
+ case TEGRA30_AHUB_APBIF_INT_MASK:
+ case TEGRA30_AHUB_I2S_INT_STATUS:
+ case TEGRA30_AHUB_DAM_INT_STATUS:
+ case TEGRA30_AHUB_SPDIF_INT_STATUS:
+ case TEGRA30_AHUB_APBIF_INT_STATUS:
+ case TEGRA30_AHUB_I2S_INT_SOURCE:
+ case TEGRA30_AHUB_DAM_INT_SOURCE:
+ case TEGRA30_AHUB_SPDIF_INT_SOURCE:
+ case TEGRA30_AHUB_APBIF_INT_SOURCE:
+ case TEGRA30_AHUB_I2S_INT_SET:
+ case TEGRA30_AHUB_DAM_INT_SET:
+ case TEGRA30_AHUB_SPDIF_INT_SET:
+ case TEGRA30_AHUB_APBIF_INT_SET:
+ return true;
+ default:
+ break;
+ };
+
+ if (REG_IN_ARRAY(reg, CHANNEL_CTRL) ||
+ REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+ REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+ REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+ REG_IN_ARRAY(reg, CIF_TX_CTRL) ||
+ REG_IN_ARRAY(reg, CIF_RX_CTRL) ||
+ REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+ return true;
+
+ return false;
+}
+
+static bool tegra30_ahub_apbif_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_AHUB_CONFIG_LINK_CTRL:
+ case TEGRA30_AHUB_MISC_CTRL:
+ case TEGRA30_AHUB_APBDMA_LIVE_STATUS:
+ case TEGRA30_AHUB_I2S_LIVE_STATUS:
+ case TEGRA30_AHUB_SPDIF_LIVE_STATUS:
+ case TEGRA30_AHUB_I2S_INT_STATUS:
+ case TEGRA30_AHUB_DAM_INT_STATUS:
+ case TEGRA30_AHUB_SPDIF_INT_STATUS:
+ case TEGRA30_AHUB_APBIF_INT_STATUS:
+ case TEGRA30_AHUB_I2S_INT_SET:
+ case TEGRA30_AHUB_DAM_INT_SET:
+ case TEGRA30_AHUB_SPDIF_INT_SET:
+ case TEGRA30_AHUB_APBIF_INT_SET:
+ return true;
+ default:
+ break;
+ };
+
+ if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+ REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+ REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+ REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+ return true;
+
+ return false;
+}
+
+static bool tegra30_ahub_apbif_precious_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO))
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config tegra30_ahub_apbif_regmap_config = {
+ .name = "apbif",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA30_AHUB_APBIF_INT_SET,
+ .writeable_reg = tegra30_ahub_apbif_wr_rd_reg,
+ .readable_reg = tegra30_ahub_apbif_wr_rd_reg,
+ .volatile_reg = tegra30_ahub_apbif_volatile_reg,
+ .precious_reg = tegra30_ahub_apbif_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ if (REG_IN_ARRAY(reg, AUDIO_RX))
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config tegra30_ahub_ahub_regmap_config = {
+ .name = "ahub",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = LAST_REG(AUDIO_RX),
+ .writeable_reg = tegra30_ahub_ahub_wr_rd_reg,
+ .readable_reg = tegra30_ahub_ahub_wr_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ int i;
+ struct resource *res0, *res1, *region;
+ u32 of_dma[2];
+ void __iomem *regs_apbif, *regs_ahub;
+ int ret = 0;
+
+ if (ahub)
+ return -ENODEV;
+
+ /*
+ * The AHUB hosts a register bus: the "configlink". For this to
+ * operate correctly, all devices on this bus must be out of reset.
+ * Ensure that here.
+ */
+ for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
+ clk = clk_get_sys(NULL, configlink_clocks[i]);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Can't get clock %s\n",
+ configlink_clocks[i]);
+ ret = PTR_ERR(clk);
+ goto err;
+ }
+ tegra_periph_reset_deassert(clk);
+ clk_put(clk);
+ }
+
+ ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
+ GFP_KERNEL);
+ if (!ahub) {
+ dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, ahub);
+
+ ahub->dev = &pdev->dev;
+
+ ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio");
+ if (IS_ERR(ahub->clk_d_audio)) {
+ dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n");
+ ret = PTR_ERR(ahub->clk_d_audio);
+ goto err;
+ }
+
+ ahub->clk_apbif = clk_get(&pdev->dev, "apbif");
+ if (IS_ERR(ahub->clk_apbif)) {
+ dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n");
+ ret = PTR_ERR(ahub->clk_apbif);
+ goto err_clk_put_d_audio;
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "nvidia,dma-request-selector",
+ of_dma, 2) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,dma-request-selector\n");
+ ret = -ENODEV;
+ goto err_clk_put_d_audio;
+ }
+ ahub->dma_sel = of_dma[1];
+
+ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res0) {
+ dev_err(&pdev->dev, "No apbif memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put_apbif;
+ }
+
+ region = devm_request_mem_region(&pdev->dev, res0->start,
+ resource_size(res0), DRV_NAME);
+ if (!region) {
+ dev_err(&pdev->dev, "request region apbif failed\n");
+ ret = -EBUSY;
+ goto err_clk_put_apbif;
+ }
+ ahub->apbif_addr = res0->start;
+
+ regs_apbif = devm_ioremap(&pdev->dev, res0->start,
+ resource_size(res0));
+ if (!regs_apbif) {
+ dev_err(&pdev->dev, "ioremap apbif failed\n");
+ ret = -ENOMEM;
+ goto err_clk_put_apbif;
+ }
+
+ ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif,
+ &tegra30_ahub_apbif_regmap_config);
+ if (IS_ERR(ahub->regmap_apbif)) {
+ dev_err(&pdev->dev, "apbif regmap init failed\n");
+ ret = PTR_ERR(ahub->regmap_apbif);
+ goto err_clk_put_apbif;
+ }
+ regcache_cache_only(ahub->regmap_apbif, true);
+
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res1) {
+ dev_err(&pdev->dev, "No ahub memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put_apbif;
+ }
+
+ region = devm_request_mem_region(&pdev->dev, res1->start,
+ resource_size(res1), DRV_NAME);
+ if (!region) {
+ dev_err(&pdev->dev, "request region ahub failed\n");
+ ret = -EBUSY;
+ goto err_clk_put_apbif;
+ }
+
+ regs_ahub = devm_ioremap(&pdev->dev, res1->start,
+ resource_size(res1));
+ if (!regs_ahub) {
+ dev_err(&pdev->dev, "ioremap ahub failed\n");
+ ret = -ENOMEM;
+ goto err_clk_put_apbif;
+ }
+
+ ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub,
+ &tegra30_ahub_ahub_regmap_config);
+ if (IS_ERR(ahub->regmap_ahub)) {
+ dev_err(&pdev->dev, "ahub regmap init failed\n");
+ ret = PTR_ERR(ahub->regmap_ahub);
+ goto err_clk_put_apbif;
+ }
+ regcache_cache_only(ahub->regmap_ahub, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra30_ahub_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata,
+ &pdev->dev);
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put_apbif:
+ clk_put(ahub->clk_apbif);
+err_clk_put_d_audio:
+ clk_put(ahub->clk_d_audio);
+ ahub = 0;
+err:
+ return ret;
+}
+
+static int __devexit tegra30_ahub_remove(struct platform_device *pdev)
+{
+ if (!ahub)
+ return -ENODEV;
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_ahub_runtime_suspend(&pdev->dev);
+
+ clk_put(ahub->clk_apbif);
+ clk_put(ahub->clk_d_audio);
+
+ ahub = 0;
+
+ return 0;
+}
+
+static const struct of_device_id tegra30_ahub_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra30-ahub", },
+ {},
+};
+
+static const struct dev_pm_ops tegra30_ahub_pm_ops __devinitconst = {
+ SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
+ tegra30_ahub_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_ahub_driver = {
+ .probe = tegra30_ahub_probe,
+ .remove = __devexit_p(tegra30_ahub_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra30_ahub_of_match,
+ .pm = &tegra30_ahub_pm_ops,
+ },
+};
+module_platform_driver(tegra30_ahub_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 AHUB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
new file mode 100644
index 00000000000..e690e2eecc9
--- /dev/null
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -0,0 +1,483 @@
+/*
+ * tegra30_ahub.h - Definitions for Tegra30 AHUB driver
+ *
+ * Copyright (c) 2011,2012, 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/>.
+ */
+
+#ifndef __TEGRA30_AHUB_H__
+#define __TEGRA30_AHUB_H__
+
+/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
+
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 28
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 24
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 7
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_BITS_4 0
+#define TEGRA30_AUDIOCIF_BITS_8 1
+#define TEGRA30_AUDIOCIF_BITS_12 2
+#define TEGRA30_AUDIOCIF_BITS_16 3
+#define TEGRA30_AUDIOCIF_BITS_20 4
+#define TEGRA30_AUDIOCIF_BITS_24 5
+#define TEGRA30_AUDIOCIF_BITS_28 6
+#define TEGRA30_AUDIOCIF_BITS_32 7
+
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT 12
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT 8
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_EXPAND_ZERO 0
+#define TEGRA30_AUDIOCIF_EXPAND_ONE 1
+#define TEGRA30_AUDIOCIF_EXPAND_LFSR 2
+
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT 6
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK (3 << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO (TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE (TEGRA30_AUDIOCIF_EXPAND_ONE << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR (TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0 0
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1 1
+#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG 2
+
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT 4
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK (3 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0 (TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1 (TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG (TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_REPLICATE 3
+
+#define TEGRA30_AUDIOCIF_DIRECTION_TX 0
+#define TEGRA30_AUDIOCIF_DIRECTION_RX 1
+
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT 2
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK (1 << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX (TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX (TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+
+#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND 0
+#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP 1
+
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT 1
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK (1 << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND (TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP (TEGRA30_AUDIOCIF_TRUNCATE_CHOP << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+
+#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO 0
+#define TEGRA30_AUDIOCIF_MONO_CONV_COPY 1
+
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT 0
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK (1 << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO (TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY (TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+
+/* Registers within TEGRA30_AUDIO_CLUSTER_BASE */
+
+/* TEGRA30_AHUB_CHANNEL_CTRL */
+
+#define TEGRA30_AHUB_CHANNEL_CTRL 0x0
+#define TEGRA30_AHUB_CHANNEL_CTRL_STRIDE 0x20
+#define TEGRA30_AHUB_CHANNEL_CTRL_COUNT 4
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_EN (1 << 31)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_EN (1 << 30)
+#define TEGRA30_AHUB_CHANNEL_CTRL_LOOPBACK (1 << 29)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT 16
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US 0xff
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT 8
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US 0xff
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN (1 << 6)
+
+#define TEGRA30_PACK_8_4 2
+#define TEGRA30_PACK_16 3
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT 4
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US 3
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN (1 << 2)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT 0
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US 3
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK (TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4 (TEGRA30_PACK_8_4 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16 (TEGRA30_PACK_16 << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+
+/* TEGRA30_AHUB_CHANNEL_CLEAR */
+
+#define TEGRA30_AHUB_CHANNEL_CLEAR 0x4
+#define TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE 0x20
+#define TEGRA30_AHUB_CHANNEL_CLEAR_COUNT 4
+#define TEGRA30_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET (1 << 31)
+#define TEGRA30_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET (1 << 30)
+
+/* TEGRA30_AHUB_CHANNEL_STATUS */
+
+#define TEGRA30_AHUB_CHANNEL_STATUS 0x8
+#define TEGRA30_AHUB_CHANNEL_STATUS_STRIDE 0x20
+#define TEGRA30_AHUB_CHANNEL_STATUS_COUNT 4
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT 24
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US 0xff
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT 16
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US 0xff
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK (TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_TRIG (1 << 1)
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_TRIG (1 << 0)
+
+/* TEGRA30_AHUB_CHANNEL_TXFIFO */
+
+#define TEGRA30_AHUB_CHANNEL_TXFIFO 0xc
+#define TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE 0x20
+#define TEGRA30_AHUB_CHANNEL_TXFIFO_COUNT 4
+
+/* TEGRA30_AHUB_CHANNEL_RXFIFO */
+
+#define TEGRA30_AHUB_CHANNEL_RXFIFO 0x10
+#define TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE 0x20
+#define TEGRA30_AHUB_CHANNEL_RXFIFO_COUNT 4
+
+/* TEGRA30_AHUB_CIF_TX_CTRL */
+
+#define TEGRA30_AHUB_CIF_TX_CTRL 0x14
+#define TEGRA30_AHUB_CIF_TX_CTRL_STRIDE 0x20
+#define TEGRA30_AHUB_CIF_TX_CTRL_COUNT 4
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */
+
+/* TEGRA30_AHUB_CIF_RX_CTRL */
+
+#define TEGRA30_AHUB_CIF_RX_CTRL 0x18
+#define TEGRA30_AHUB_CIF_RX_CTRL_STRIDE 0x20
+#define TEGRA30_AHUB_CIF_RX_CTRL_COUNT 4
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */
+
+/* TEGRA30_AHUB_CONFIG_LINK_CTRL */
+
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL 0x80
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT 28
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US 0xf
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT 16
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US 0xfff
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT 4
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US 0xfff
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK (TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CG_EN (1 << 2)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CLEAR_TIMEOUT_CNTR (1 << 1)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_SOFT_RESET (1 << 0)
+
+/* TEGRA30_AHUB_MISC_CTRL */
+
+#define TEGRA30_AHUB_MISC_CTRL 0x84
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_ACTIVE (1 << 31)
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_CG_EN (1 << 8)
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT 0
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_MASK (0x1f << TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT)
+
+/* TEGRA30_AHUB_APBDMA_LIVE_STATUS */
+
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS 0x88
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_FULL (1 << 31)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_FULL (1 << 30)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_FULL (1 << 29)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_FULL (1 << 28)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_FULL (1 << 27)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_FULL (1 << 26)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_FULL (1 << 25)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL (1 << 24)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_EMPTY (1 << 23)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_EMPTY (1 << 22)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_EMPTY (1 << 21)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_EMPTY (1 << 20)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_EMPTY (1 << 19)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_EMPTY (1 << 18)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_EMPTY (1 << 17)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_EMPTY (1 << 16)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_FULL (1 << 15)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_FULL (1 << 14)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_FULL (1 << 13)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_FULL (1 << 12)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_FULL (1 << 11)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_FULL (1 << 10)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_FULL (1 << 9)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_FULL (1 << 8)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_EMPTY (1 << 7)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_EMPTY (1 << 6)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_EMPTY (1 << 5)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_EMPTY (1 << 4)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_EMPTY (1 << 3)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_EMPTY (1 << 2)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_EMPTY (1 << 1)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_EMPTY (1 << 0)
+
+/* TEGRA30_AHUB_I2S_LIVE_STATUS */
+
+#define TEGRA30_AHUB_I2S_LIVE_STATUS 0x8c
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_FULL (1 << 29)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_FULL (1 << 28)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_FULL (1 << 27)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_FULL (1 << 26)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_FULL (1 << 25)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_FULL (1 << 24)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_FULL (1 << 23)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_FULL (1 << 22)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_FULL (1 << 21)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_FULL (1 << 20)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_ENABLED (1 << 19)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_ENABLED (1 << 18)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_ENABLED (1 << 17)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_ENABLED (1 << 16)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_ENABLED (1 << 15)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_ENABLED (1 << 14)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_ENABLED (1 << 13)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_ENABLED (1 << 12)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED (1 << 11)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED (1 << 10)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_EMPTY (1 << 9)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_EMPTY (1 << 8)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_EMPTY (1 << 7)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_EMPTY (1 << 6)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_EMPTY (1 << 5)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_EMPTY (1 << 4)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_EMPTY (1 << 3)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_EMPTY (1 << 2)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY (1 << 1)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY (1 << 0)
+
+/* TEGRA30_AHUB_DAM0_LIVE_STATUS */
+
+#define TEGRA30_AHUB_DAM_LIVE_STATUS 0x90
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE 0x8
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_COUNT 3
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TX_ENABLED (1 << 26)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1_ENABLED (1 << 25)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0_ENABLED (1 << 24)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_FULL (1 << 15)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_FULL (1 << 9)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_FULL (1 << 8)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_EMPTY (1 << 7)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_EMPTY (1 << 1)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_EMPTY (1 << 0)
+
+/* TEGRA30_AHUB_SPDIF_LIVE_STATUS */
+
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS 0xa8
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TX_ENABLED (1 << 11)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RX_ENABLED (1 << 10)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TX_ENABLED (1 << 9)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RX_ENABLED (1 << 8)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_FULL (1 << 7)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_FULL (1 << 6)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_FULL (1 << 5)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_FULL (1 << 4)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_EMPTY (1 << 3)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_EMPTY (1 << 2)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_EMPTY (1 << 1)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_EMPTY (1 << 0)
+
+/* TEGRA30_AHUB_I2S_INT_MASK */
+
+#define TEGRA30_AHUB_I2S_INT_MASK 0xb0
+
+/* TEGRA30_AHUB_DAM_INT_MASK */
+
+#define TEGRA30_AHUB_DAM_INT_MASK 0xb4
+
+/* TEGRA30_AHUB_SPDIF_INT_MASK */
+
+#define TEGRA30_AHUB_SPDIF_INT_MASK 0xbc
+
+/* TEGRA30_AHUB_APBIF_INT_MASK */
+
+#define TEGRA30_AHUB_APBIF_INT_MASK 0xc0
+
+/* TEGRA30_AHUB_I2S_INT_STATUS */
+
+#define TEGRA30_AHUB_I2S_INT_STATUS 0xc8
+
+/* TEGRA30_AHUB_DAM_INT_STATUS */
+
+#define TEGRA30_AHUB_DAM_INT_STATUS 0xcc
+
+/* TEGRA30_AHUB_SPDIF_INT_STATUS */
+
+#define TEGRA30_AHUB_SPDIF_INT_STATUS 0xd4
+
+/* TEGRA30_AHUB_APBIF_INT_STATUS */
+
+#define TEGRA30_AHUB_APBIF_INT_STATUS 0xd8
+
+/* TEGRA30_AHUB_I2S_INT_SOURCE */
+
+#define TEGRA30_AHUB_I2S_INT_SOURCE 0xe0
+
+/* TEGRA30_AHUB_DAM_INT_SOURCE */
+
+#define TEGRA30_AHUB_DAM_INT_SOURCE 0xe4
+
+/* TEGRA30_AHUB_SPDIF_INT_SOURCE */
+
+#define TEGRA30_AHUB_SPDIF_INT_SOURCE 0xec
+
+/* TEGRA30_AHUB_APBIF_INT_SOURCE */
+
+#define TEGRA30_AHUB_APBIF_INT_SOURCE 0xf0
+
+/* TEGRA30_AHUB_I2S_INT_SET */
+
+#define TEGRA30_AHUB_I2S_INT_SET 0xf8
+
+/* TEGRA30_AHUB_DAM_INT_SET */
+
+#define TEGRA30_AHUB_DAM_INT_SET 0xfc
+
+/* TEGRA30_AHUB_SPDIF_INT_SET */
+
+#define TEGRA30_AHUB_SPDIF_INT_SET 0x100
+
+/* TEGRA30_AHUB_APBIF_INT_SET */
+
+#define TEGRA30_AHUB_APBIF_INT_SET 0x104
+
+/* Registers within TEGRA30_AHUB_BASE */
+
+#define TEGRA30_AHUB_AUDIO_RX 0x0
+#define TEGRA30_AHUB_AUDIO_RX_STRIDE 0x4
+#define TEGRA30_AHUB_AUDIO_RX_COUNT 17
+/* This register repeats once for each entry in enum tegra30_ahub_rxcif */
+/* The fields in this register are 1 bit per entry in tegra30_ahub_txcif */
+
+/*
+ * Terminology:
+ * AHUB: Audio Hub; a cross-bar switch between the audio devices: DMA FIFOs,
+ * I2S controllers, SPDIF controllers, and DAMs.
+ * XBAR: The core cross-bar component of the AHUB.
+ * CIF: Client Interface; the HW module connecting an audio device to the
+ * XBAR.
+ * DAM: Digital Audio Mixer: A HW module that mixes multiple audio streams,
+ * possibly including sample-rate conversion.
+ *
+ * Each TX CIF transmits data into the XBAR. Each RX CIF can receive audio
+ * transmitted by a particular TX CIF.
+ *
+ * This driver is currently very simplistic; many HW features are not
+ * exposed; DAMs are not supported, only 16-bit stereo audio is supported,
+ * etc.
+ */
+
+enum tegra30_ahub_txcif {
+ TEGRA30_AHUB_TXCIF_APBIF_TX0,
+ TEGRA30_AHUB_TXCIF_APBIF_TX1,
+ TEGRA30_AHUB_TXCIF_APBIF_TX2,
+ TEGRA30_AHUB_TXCIF_APBIF_TX3,
+ TEGRA30_AHUB_TXCIF_I2S0_TX0,
+ TEGRA30_AHUB_TXCIF_I2S1_TX0,
+ TEGRA30_AHUB_TXCIF_I2S2_TX0,
+ TEGRA30_AHUB_TXCIF_I2S3_TX0,
+ TEGRA30_AHUB_TXCIF_I2S4_TX0,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0,
+ TEGRA30_AHUB_TXCIF_DAM1_TX0,
+ TEGRA30_AHUB_TXCIF_DAM2_TX0,
+ TEGRA30_AHUB_TXCIF_SPDIF_TX0,
+ TEGRA30_AHUB_TXCIF_SPDIF_TX1,
+};
+
+enum tegra30_ahub_rxcif {
+ TEGRA30_AHUB_RXCIF_APBIF_RX0,
+ TEGRA30_AHUB_RXCIF_APBIF_RX1,
+ TEGRA30_AHUB_RXcIF_APBIF_RX2,
+ TEGRA30_AHUB_RXCIF_APBIF_RX3,
+ TEGRA30_AHUB_RXCIF_I2S0_RX0,
+ TEGRA30_AHUB_RXCIF_I2S1_RX0,
+ TEGRA30_AHUB_RXCIF_I2S2_RX0,
+ TEGRA30_AHUB_RXCIF_I2S3_RX0,
+ TEGRA30_AHUB_RXCIF_I2S4_RX0,
+ TEGRA30_AHUB_RXCIF_DAM0_RX0,
+ TEGRA30_AHUB_RXCIF_DAM0_RX1,
+ TEGRA30_AHUB_RXCIF_DAM1_RX0,
+ TEGRA30_AHUB_RXCIF_DAM2_RX1,
+ TEGRA30_AHUB_RXCIF_DAM3_RX0,
+ TEGRA30_AHUB_RXCIF_DAM3_RX1,
+ TEGRA30_AHUB_RXCIF_SPDIF_RX0,
+ TEGRA30_AHUB_RXCIF_SPDIF_RX1,
+};
+
+extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
+ unsigned long *fiforeg,
+ unsigned long *reqsel);
+extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+
+extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
+ unsigned long *fiforeg,
+ unsigned long *reqsel);
+extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif);
+
+extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
+ enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+
+struct tegra30_ahub {
+ struct device *dev;
+ struct clk *clk_d_audio;
+ struct clk *clk_apbif;
+ int dma_sel;
+ resource_size_t apbif_addr;
+ struct regmap *regmap_apbif;
+ struct regmap *regmap_ahub;
+ DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+ DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
new file mode 100644
index 00000000000..8596032985d
--- /dev/null
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -0,0 +1,536 @@
+/*
+ * tegra30_i2s.c - Tegra30 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * 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/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra30_ahub.h"
+#include "tegra30_i2s.h"
+
+#define DRV_NAME "tegra30-i2s"
+
+static inline void tegra30_i2s_write(struct tegra30_i2s *i2s, u32 reg, u32 val)
+{
+ regmap_write(i2s->regmap, reg, val);
+}
+
+static inline u32 tegra30_i2s_read(struct tegra30_i2s *i2s, u32 reg)
+{
+ u32 val;
+ regmap_read(i2s->regmap, reg, &val);
+ return val;
+}
+
+static int tegra30_i2s_runtime_suspend(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+
+ clk_disable(i2s->clk_i2s);
+
+ return 0;
+}
+
+static int tegra30_i2s_runtime_resume(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(i2s->clk_i2s);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(i2s->regmap, false);
+
+ return 0;
+}
+
+int tegra30_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
+ &i2s->playback_dma_data.addr,
+ &i2s->playback_dma_data.req_sel);
+ i2s->playback_dma_data.wrap = 4;
+ i2s->playback_dma_data.width = 32;
+ tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif,
+ i2s->playback_fifo_cif);
+ } else {
+ ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif,
+ &i2s->capture_dma_data.addr,
+ &i2s->capture_dma_data.req_sel);
+ i2s->capture_dma_data.wrap = 4;
+ i2s->capture_dma_data.width = 32;
+ tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif,
+ i2s->capture_i2s_cif);
+ }
+
+ return ret;
+}
+
+void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
+ tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
+ } else {
+ tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif);
+ tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
+ }
+}
+
+static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_MASTER_ENABLE;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
+ TEGRA30_I2S_CTRL_LRCK_MASK);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_R_LOW;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ int ret, sample_size, srate, i2sclock, bitcnt;
+
+ if (params_channels(params) != 2)
+ return -EINVAL;
+
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16;
+ sample_size = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ srate = params_rate(params);
+
+ /* Final "* 2" required by Tegra hardware */
+ i2sclock = srate * params_channels(params) * sample_size * 2;
+
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ if (bitcnt < 0 || bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+ return -EINVAL;
+
+ ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (i2sclock % (2 * srate))
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+ } else {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val);
+ }
+
+ val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+ (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
+ tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val);
+
+ return 0;
+}
+
+static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s)
+{
+ tegra30_ahub_enable_tx_fifo(i2s->playback_fifo_cif);
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
+{
+ tegra30_ahub_disable_tx_fifo(i2s->playback_fifo_cif);
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
+{
+ tegra30_ahub_enable_rx_fifo(i2s->capture_fifo_cif);
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
+{
+ tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif);
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra30_i2s_start_playback(i2s);
+ else
+ tegra30_i2s_start_capture(i2s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra30_i2s_stop_playback(i2s);
+ else
+ tegra30_i2s_stop_capture(i2s);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra30_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = &i2s->capture_dma_data;
+ dai->playback_dma_data = &i2s->playback_dma_data;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
+ .startup = tegra30_i2s_startup,
+ .shutdown = tegra30_i2s_shutdown,
+ .set_fmt = tegra30_i2s_set_fmt,
+ .hw_params = tegra30_i2s_hw_params,
+ .trigger = tegra30_i2s_trigger,
+};
+
+static const struct snd_soc_dai_driver tegra30_i2s_dai_template = {
+ .probe = tegra30_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra30_i2s_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_I2S_CTRL:
+ case TEGRA30_I2S_TIMING:
+ case TEGRA30_I2S_OFFSET:
+ case TEGRA30_I2S_CH_CTRL:
+ case TEGRA30_I2S_SLOT_CTRL:
+ case TEGRA30_I2S_CIF_RX_CTRL:
+ case TEGRA30_I2S_CIF_TX_CTRL:
+ case TEGRA30_I2S_FLOWCTL:
+ case TEGRA30_I2S_TX_STEP:
+ case TEGRA30_I2S_FLOW_STATUS:
+ case TEGRA30_I2S_FLOW_TOTAL:
+ case TEGRA30_I2S_FLOW_OVER:
+ case TEGRA30_I2S_FLOW_UNDER:
+ case TEGRA30_I2S_LCOEF_1_4_0:
+ case TEGRA30_I2S_LCOEF_1_4_1:
+ case TEGRA30_I2S_LCOEF_1_4_2:
+ case TEGRA30_I2S_LCOEF_1_4_3:
+ case TEGRA30_I2S_LCOEF_1_4_4:
+ case TEGRA30_I2S_LCOEF_1_4_5:
+ case TEGRA30_I2S_LCOEF_2_4_0:
+ case TEGRA30_I2S_LCOEF_2_4_1:
+ case TEGRA30_I2S_LCOEF_2_4_2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_I2S_FLOW_STATUS:
+ case TEGRA30_I2S_FLOW_TOTAL:
+ case TEGRA30_I2S_FLOW_OVER:
+ case TEGRA30_I2S_FLOW_UNDER:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra30_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA30_I2S_LCOEF_2_4_2,
+ .writeable_reg = tegra30_i2s_wr_rd_reg,
+ .readable_reg = tegra30_i2s_wr_rd_reg,
+ .volatile_reg = tegra30_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
+{
+ struct tegra30_i2s *i2s;
+ u32 cif_ids[2];
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL);
+ if (!i2s) {
+ dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ i2s->dai = tegra30_i2s_dai_template;
+ i2s->dai.name = dev_name(&pdev->dev);
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "nvidia,ahub-cif-ids", cif_ids,
+ ARRAY_SIZE(cif_ids));
+ if (ret < 0)
+ goto err;
+
+ i2s->playback_i2s_cif = cif_ids[0];
+ i2s->capture_i2s_cif = cif_ids[1];
+
+ i2s->clk_i2s = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2s->clk_i2s)) {
+ dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ 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;
+ goto err_clk_put;
+ }
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra30_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(i2s->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(i2s->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra30_i2s_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ ret = -ENOMEM;
+ goto err_suspend;
+ }
+
+ ret = tegra_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_dai;
+ }
+
+ return 0;
+
+err_unregister_dai:
+ snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ clk_put(i2s->clk_i2s);
+err:
+ return ret;
+}
+
+static int __devexit tegra30_i2s_platform_remove(struct platform_device *pdev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_i2s_runtime_suspend(&pdev->dev);
+
+ tegra_pcm_platform_unregister(&pdev->dev);
+ snd_soc_unregister_dai(&pdev->dev);
+
+ clk_put(i2s->clk_i2s);
+
+ return 0;
+}
+
+static const struct of_device_id tegra30_i2s_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra30-i2s", },
+ {},
+};
+
+static const struct dev_pm_ops tegra30_i2s_pm_ops __devinitconst = {
+ SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
+ tegra30_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra30_i2s_of_match,
+ .pm = &tegra30_i2s_pm_ops,
+ },
+ .probe = tegra30_i2s_platform_probe,
+ .remove = __devexit_p(tegra30_i2s_platform_remove),
+};
+module_platform_driver(tegra30_i2s_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match);
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
new file mode 100644
index 00000000000..91adf29c7a8
--- /dev/null
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -0,0 +1,242 @@
+/*
+ * tegra30_i2s.h - Definitions for Tegra30 I2S driver
+ *
+ * Copyright (c) 2011,2012, 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/>.
+ */
+
+#ifndef __TEGRA30_I2S_H__
+#define __TEGRA30_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA30_I2S*_BASE */
+
+#define TEGRA30_I2S_CTRL 0x0
+#define TEGRA30_I2S_TIMING 0x4
+#define TEGRA30_I2S_OFFSET 0x08
+#define TEGRA30_I2S_CH_CTRL 0x0c
+#define TEGRA30_I2S_SLOT_CTRL 0x10
+#define TEGRA30_I2S_CIF_RX_CTRL 0x14
+#define TEGRA30_I2S_CIF_TX_CTRL 0x18
+#define TEGRA30_I2S_FLOWCTL 0x1c
+#define TEGRA30_I2S_TX_STEP 0x20
+#define TEGRA30_I2S_FLOW_STATUS 0x24
+#define TEGRA30_I2S_FLOW_TOTAL 0x28
+#define TEGRA30_I2S_FLOW_OVER 0x2c
+#define TEGRA30_I2S_FLOW_UNDER 0x30
+#define TEGRA30_I2S_LCOEF_1_4_0 0x34
+#define TEGRA30_I2S_LCOEF_1_4_1 0x38
+#define TEGRA30_I2S_LCOEF_1_4_2 0x3c
+#define TEGRA30_I2S_LCOEF_1_4_3 0x40
+#define TEGRA30_I2S_LCOEF_1_4_4 0x44
+#define TEGRA30_I2S_LCOEF_1_4_5 0x48
+#define TEGRA30_I2S_LCOEF_2_4_0 0x4c
+#define TEGRA30_I2S_LCOEF_2_4_1 0x50
+#define TEGRA30_I2S_LCOEF_2_4_2 0x54
+
+/* Fields in TEGRA30_I2S_CTRL */
+
+#define TEGRA30_I2S_CTRL_XFER_EN_TX (1 << 31)
+#define TEGRA30_I2S_CTRL_XFER_EN_RX (1 << 30)
+#define TEGRA30_I2S_CTRL_CG_EN (1 << 29)
+#define TEGRA30_I2S_CTRL_SOFT_RESET (1 << 28)
+#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN (1 << 27)
+
+#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT 24
+#define TEGRA30_I2S_CTRL_OBS_SEL_MASK (7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT)
+
+#define TEGRA30_I2S_FRAME_FORMAT_LRCK 0
+#define TEGRA30_I2S_FRAME_FORMAT_FSYNC 1
+
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT 12
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK (7 << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK (TEGRA30_I2S_FRAME_FORMAT_LRCK << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC (TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+
+#define TEGRA30_I2S_CTRL_MASTER_ENABLE (1 << 10)
+
+#define TEGRA30_I2S_LRCK_LEFT_LOW 0
+#define TEGRA30_I2S_LRCK_RIGHT_LOW 1
+
+#define TEGRA30_I2S_CTRL_LRCK_SHIFT 9
+#define TEGRA30_I2S_CTRL_LRCK_MASK (1 << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_L_LOW (TEGRA30_I2S_LRCK_LEFT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_R_LOW (TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA30_I2S_CTRL_LPBK_ENABLE (1 << 8)
+
+#define TEGRA30_I2S_BIT_CODE_LINEAR 0
+#define TEGRA30_I2S_BIT_CODE_ULAW 1
+#define TEGRA30_I2S_BIT_CODE_ALAW 2
+
+#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT 4
+#define TEGRA30_I2S_CTRL_BIT_CODE_MASK (3 << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR (TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW (TEGRA30_I2S_BIT_CODE_ULAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW (TEGRA30_I2S_BIT_CODE_ALAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+
+#define TEGRA30_I2S_BITS_8 1
+#define TEGRA30_I2S_BITS_12 2
+#define TEGRA30_I2S_BITS_16 3
+#define TEGRA30_I2S_BITS_20 4
+#define TEGRA30_I2S_BITS_24 5
+#define TEGRA30_I2S_BITS_28 6
+#define TEGRA30_I2S_BITS_32 7
+
+/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */
+#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT 0
+#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK (7 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_8 (TEGRA30_I2S_BITS_8 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_12 (TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_16 (TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_20 (TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_24 (TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_28 (TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_32 (TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+
+/* Fields in TEGRA30_I2S_TIMING */
+
+#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA30_I2S_OFFSET */
+
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT 16
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US 0x7ff
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT)
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT 0
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US 0x7ff
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT)
+
+/* Fields in TEGRA30_I2S_CH_CTRL */
+
+/* (FSYNC width - 1) in bit clocks */
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT 24
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US 0xff
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK (TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT)
+
+#define TEGRA30_I2S_HIGHZ_NO 0
+#define TEGRA30_I2S_HIGHZ_YES 1
+#define TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK 2
+
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT 12
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_MASK (3 << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_NO (TEGRA30_I2S_HIGHZ_NO << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_YES (TEGRA30_I2S_HIGHZ_YES << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_ON_HALF_BIT_CLK (TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+
+#define TEGRA30_I2S_MSB_FIRST 0
+#define TEGRA30_I2S_LSB_FIRST 1
+
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT 10
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT 9
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+
+#define TEGRA30_I2S_POS_EDGE 0
+#define TEGRA30_I2S_NEG_EDGE 1
+
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT 8
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK (1 << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE (TEGRA30_I2S_POS_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE (TEGRA30_I2S_NEG_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+
+/* Sample size is # bits from BIT_SIZE minus this field */
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT 4
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US 7
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT)
+
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT 0
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US 7
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT)
+
+/* Fields in TEGRA30_I2S_SLOT_CTRL */
+
+/* Number of slots in frame, minus 1 */
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT)
+
+/* TDM mode slot enable bitmask */
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT)
+
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT 0
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK (0xff << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT)
+
+/* Fields in TEGRA30_I2S_CIF_RX_CTRL */
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */
+
+/* Fields in TEGRA30_I2S_CIF_TX_CTRL */
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */
+
+/* Fields in TEGRA30_I2S_FLOWCTL */
+
+#define TEGRA30_I2S_FILTER_LINEAR 0
+#define TEGRA30_I2S_FILTER_QUAD 1
+
+#define TEGRA30_I2S_FLOWCTL_FILTER_SHIFT 31
+#define TEGRA30_I2S_FLOWCTL_FILTER_MASK (1 << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_LINEAR (TEGRA30_I2S_FILTER_LINEAR << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_QUAD (TEGRA30_I2S_FILTER_QUAD << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+
+/* Fields in TEGRA30_I2S_TX_STEP */
+
+#define TEGRA30_I2S_TX_STEP_SHIFT 0
+#define TEGRA30_I2S_TX_STEP_MASK_US 0xffff
+#define TEGRA30_I2S_TX_STEP_MASK (TEGRA30_I2S_TX_STEP_MASK_US << TEGRA30_I2S_TX_STEP_SHIFT)
+
+/* Fields in TEGRA30_I2S_FLOW_STATUS */
+
+#define TEGRA30_I2S_FLOW_STATUS_UNDERFLOW (1 << 31)
+#define TEGRA30_I2S_FLOW_STATUS_OVERFLOW (1 << 30)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_INT_EN (1 << 4)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_CLR (1 << 3)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_CLR (1 << 2)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_EN (1 << 1)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_EN (1 << 0)
+
+/*
+ * There are no fields in TEGRA30_I2S_FLOW_TOTAL, TEGRA30_I2S_FLOW_OVER,
+ * TEGRA30_I2S_FLOW_UNDER; they are counters taking the whole register.
+ */
+
+/* Fields in TEGRA30_I2S_LCOEF_* */
+
+#define TEGRA30_I2S_LCOEF_COEF_SHIFT 0
+#define TEGRA30_I2S_LCOEF_COEF_MASK_US 0xffff
+#define TEGRA30_I2S_LCOEF_COEF_MASK (TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT)
+
+struct tegra30_i2s {
+ struct snd_soc_dai_driver dai;
+ int cif_id;
+ struct clk *clk_i2s;
+ enum tegra30_ahub_txcif capture_i2s_cif;
+ enum tegra30_ahub_rxcif capture_fifo_cif;
+ struct tegra_pcm_dma_params capture_dma_data;
+ enum tegra30_ahub_rxcif playback_i2s_cif;
+ enum tegra30_ahub_txcif playback_fifo_cif;
+ struct tegra_pcm_dma_params playback_dma_data;
+ struct regmap *regmap;
+ u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index e45ccd851f6..32de7006daf 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -1,16 +1,17 @@
/*
-* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver
-*
-* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
-*
-* Authors: Leon Romanovsky <leon@leon.nu>
-* Andrey Danin <danindrey@mail.ru>
-* Marc Dietrich <marvin24@gmx.de>
-*
-* 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.
-*/
+ * tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+ * Copyright (C) 2012 - NVIDIA, Inc.
+ *
+ * Authors: Leon Romanovsky <leon@leon.nu>
+ * Andrey Danin <danindrey@mail.ru>
+ * Marc Dietrich <marvin24@gmx.de>
+ *
+ * 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 <asm/mach-types.h>
@@ -28,9 +29,6 @@
#include "../codecs/alc5632.h"
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-alc5632"
@@ -39,7 +37,6 @@
struct tegra_alc5632 {
struct tegra_asoc_utils_data util_data;
- struct platform_device *pcm_dev;
int gpio_requested;
int gpio_hp_det;
};
@@ -140,7 +137,6 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_link tegra_alc5632_dai = {
.name = "ALC5632",
.stream_name = "ALC5632 PCM",
- .platform_name = "tegra-pcm-audio",
.codec_dai_name = "alc5632-hifi",
.init = tegra_alc5632_asoc_init,
.ops = &tegra_alc5632_asoc_ops,
@@ -179,8 +175,6 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, alc5632);
- alc5632->pcm_dev = ERR_PTR(-EINVAL);
-
if (!(pdev->dev.of_node)) {
dev_err(&pdev->dev, "Must be instantiated using device tree\n");
ret = -EINVAL;
@@ -214,18 +208,11 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
goto err;
}
- alc5632->pcm_dev = platform_device_register_simple(
- "tegra-pcm-audio", -1, NULL, 0);
- if (IS_ERR(alc5632->pcm_dev)) {
- dev_err(&pdev->dev,
- "Can't instantiate tegra-pcm-audio\n");
- ret = PTR_ERR(alc5632->pcm_dev);
- goto err;
- }
+ tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node;
ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
if (ret)
- goto err_unregister;
+ goto err;
ret = snd_soc_register_card(card);
if (ret) {
@@ -238,9 +225,6 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
err_fini_utils:
tegra_asoc_utils_fini(&alc5632->util_data);
-err_unregister:
- if (!IS_ERR(alc5632->pcm_dev))
- platform_device_unregister(alc5632->pcm_dev);
err:
return ret;
}
@@ -259,8 +243,6 @@ static int __devexit tegra_alc5632_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);
- if (!IS_ERR(machine->pcm_dev))
- platform_device_unregister(machine->pcm_dev);
return 0;
}
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index f8428e410e0..9515ce58ea0 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -2,7 +2,7 @@
* tegra_asoc_utils.c - Harmony machine ASoC driver
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include "tegra_asoc_utils.h"
@@ -40,7 +41,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
case 22050:
case 44100:
case 88200:
- new_baseclock = 56448000;
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ new_baseclock = 56448000;
+ else
+ new_baseclock = 564480000;
break;
case 8000:
case 16000:
@@ -48,7 +52,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
case 48000:
case 64000:
case 96000:
- new_baseclock = 73728000;
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ new_baseclock = 73728000;
+ else
+ new_baseclock = 552960000;
break;
default:
return -EINVAL;
@@ -78,7 +85,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
return err;
}
- /* Don't set cdev1 rate; its locked to pll_a_out0 */
+ /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
err = clk_enable(data->clk_pll_a);
if (err) {
@@ -112,6 +119,17 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
data->dev = dev;
+ if (of_machine_is_compatible("nvidia,tegra20"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+ else if (of_machine_is_compatible("nvidia,tegra30"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
+ else if (!dev->of_node)
+ /* non-DT is always Tegra20 */
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+ else
+ /* DT boot, but unknown SoC */
+ return -EINVAL;
+
data->clk_pll_a = clk_get_sys(NULL, "pll_a");
if (IS_ERR(data->clk_pll_a)) {
dev_err(data->dev, "Can't retrieve clk pll_a\n");
@@ -126,15 +144,24 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
goto err_put_pll_a;
}
- data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
+ 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);
if (IS_ERR(data->clk_cdev1)) {
dev_err(data->dev, "Can't retrieve clk cdev1\n");
ret = PTR_ERR(data->clk_cdev1);
goto err_put_pll_a_out0;
}
+ ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
+ if (ret)
+ goto err_put_cdev1;
+
return 0;
+err_put_cdev1:
+ clk_put(data->clk_cdev1);
err_put_pll_a_out0:
clk_put(data->clk_pll_a_out0);
err_put_pll_a:
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index 4818195da25..44db1dbb8f2 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -2,7 +2,7 @@
* tegra_asoc_utils.h - Definitions for Tegra DAS driver
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,8 +26,14 @@
struct clk;
struct device;
+enum tegra_asoc_utils_soc {
+ TEGRA_ASOC_UTILS_SOC_TEGRA20,
+ TEGRA_ASOC_UTILS_SOC_TEGRA30,
+};
+
struct tegra_asoc_utils_data {
struct device *dev;
+ enum tegra_asoc_utils_soc soc;
struct clk *clk_pll_a;
struct clk *clk_pll_a_out0;
struct clk *clk_cdev1;
@@ -42,4 +48,3 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
#endif
-
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
deleted file mode 100644
index 3b3c1ba4d23..00000000000
--- a/sound/soc/tegra/tegra_das.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * tegra_das.c - Tegra DAS driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <mach/iomap.h>
-#include <sound/soc.h>
-#include "tegra_das.h"
-
-#define DRV_NAME "tegra-das"
-
-static struct tegra_das *das;
-
-static inline void tegra_das_write(u32 reg, u32 val)
-{
- __raw_writel(val, das->regs + reg);
-}
-
-static inline u32 tegra_das_read(u32 reg)
-{
- return __raw_readl(das->regs + reg);
-}
-
-int tegra_das_connect_dap_to_dac(int dap, int dac)
-{
- u32 addr;
- u32 reg;
-
- if (!das)
- return -ENODEV;
-
- addr = TEGRA_DAS_DAP_CTRL_SEL +
- (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
- reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
-
- tegra_das_write(addr, reg);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
-
-int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
- int sdata1rx, int sdata2rx)
-{
- u32 addr;
- u32 reg;
-
- if (!das)
- return -ENODEV;
-
- addr = TEGRA_DAS_DAP_CTRL_SEL +
- (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
- reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
- !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
- !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
- !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
-
- tegra_das_write(addr, reg);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
-
-int tegra_das_connect_dac_to_dap(int dac, int dap)
-{
- u32 addr;
- u32 reg;
-
- if (!das)
- return -ENODEV;
-
- addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
- (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
- reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
- dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
- dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
-
- tegra_das_write(addr, reg);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_das_show(struct seq_file *s, void *unused)
-{
- int i;
- u32 addr;
- u32 reg;
-
- for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
- addr = TEGRA_DAS_DAP_CTRL_SEL +
- (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
- reg = tegra_das_read(addr);
- seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
- }
-
- for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
- addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
- (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
- reg = tegra_das_read(addr);
- seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
- i, reg);
- }
-
- return 0;
-}
-
-static int tegra_das_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tegra_das_show, inode->i_private);
-}
-
-static const struct file_operations tegra_das_debug_fops = {
- .open = tegra_das_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void tegra_das_debug_add(struct tegra_das *das)
-{
- das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
- snd_soc_debugfs_root, das,
- &tegra_das_debug_fops);
-}
-
-static void tegra_das_debug_remove(struct tegra_das *das)
-{
- if (das->debug)
- debugfs_remove(das->debug);
-}
-#else
-static inline void tegra_das_debug_add(struct tegra_das *das)
-{
-}
-
-static inline void tegra_das_debug_remove(struct tegra_das *das)
-{
-}
-#endif
-
-static int __devinit tegra_das_probe(struct platform_device *pdev)
-{
- struct resource *res, *region;
- int ret = 0;
-
- if (das)
- return -ENODEV;
-
- das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL);
- if (!das) {
- dev_err(&pdev->dev, "Can't allocate tegra_das\n");
- ret = -ENOMEM;
- goto err;
- }
- das->dev = &pdev->dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err;
- }
-
- region = devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err;
- }
-
- das->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!das->regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err;
- }
-
- ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1,
- TEGRA_DAS_DAP_SEL_DAC1);
- if (ret) {
- dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
- goto err;
- }
- ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1,
- TEGRA_DAS_DAC_SEL_DAP1);
- if (ret) {
- dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
- goto err;
- }
-
- tegra_das_debug_add(das);
-
- platform_set_drvdata(pdev, das);
-
- return 0;
-
-err:
- das = NULL;
- return ret;
-}
-
-static int __devexit tegra_das_remove(struct platform_device *pdev)
-{
- if (!das)
- return -ENODEV;
-
- tegra_das_debug_remove(das);
-
- das = NULL;
-
- return 0;
-}
-
-static const struct of_device_id tegra_das_of_match[] __devinitconst = {
- { .compatible = "nvidia,tegra20-das", },
- {},
-};
-
-static struct platform_driver tegra_das_driver = {
- .probe = tegra_das_probe,
- .remove = __devexit_p(tegra_das_remove),
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .of_match_table = tegra_das_of_match,
- },
-};
-module_platform_driver(tegra_das_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra DAS driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_DEVICE_TABLE(of, tegra_das_of_match);
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
deleted file mode 100644
index 2c96c7b3c45..00000000000
--- a/sound/soc/tegra/tegra_das.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * tegra_das.h - Definitions for Tegra DAS driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_DAS_H__
-#define __TEGRA_DAS_H__
-
-/* Register TEGRA_DAS_DAP_CTRL_SEL */
-#define TEGRA_DAS_DAP_CTRL_SEL 0x00
-#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5
-#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5
-
-/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
-#define TEGRA_DAS_DAP_SEL_DAC1 0
-#define TEGRA_DAS_DAP_SEL_DAC2 1
-#define TEGRA_DAS_DAP_SEL_DAC3 2
-#define TEGRA_DAS_DAP_SEL_DAP1 16
-#define TEGRA_DAS_DAP_SEL_DAP2 17
-#define TEGRA_DAS_DAP_SEL_DAP3 18
-#define TEGRA_DAS_DAP_SEL_DAP4 19
-#define TEGRA_DAS_DAP_SEL_DAP5 20
-
-/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4
-
-/*
- * Values for:
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
- */
-#define TEGRA_DAS_DAC_SEL_DAP1 0
-#define TEGRA_DAS_DAC_SEL_DAP2 1
-#define TEGRA_DAS_DAC_SEL_DAP3 2
-#define TEGRA_DAS_DAC_SEL_DAP4 3
-#define TEGRA_DAS_DAC_SEL_DAP5 4
-
-/*
- * Names/IDs of the DACs/DAPs.
- */
-
-#define TEGRA_DAS_DAP_ID_1 0
-#define TEGRA_DAS_DAP_ID_2 1
-#define TEGRA_DAS_DAP_ID_3 2
-#define TEGRA_DAS_DAP_ID_4 3
-#define TEGRA_DAS_DAP_ID_5 4
-
-#define TEGRA_DAS_DAC_ID_1 0
-#define TEGRA_DAS_DAC_ID_2 1
-#define TEGRA_DAS_DAC_ID_3 2
-
-struct tegra_das {
- struct device *dev;
- void __iomem *regs;
- struct dentry *debug;
-};
-
-/*
- * Terminology:
- * DAS: Digital audio switch (HW module controlled by this driver)
- * DAP: Digital audio port (port/pins on Tegra device)
- * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
- *
- * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
- * DAC, or another DAP. When DAPs are connected, one must be the master and
- * one the slave. Each DAC allows selection of a specific DAP for input, to
- * cater for the case where N DAPs are connected to 1 DAC for broadcast
- * output.
- *
- * This driver is dumb; no attempt is made to ensure that a valid routing
- * configuration is programmed.
- */
-
-/*
- * Connect a DAP to to a DAC
- * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
- * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
- */
-extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
-
-/*
- * Connect a DAP to to another DAP
- * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
- * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
- * master: Is this DAP the master (1) or slave (0)
- * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
- * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
- */
-extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
- int master, int sdata1rx,
- int sdata2rx);
-
-/*
- * Connect a DAC's input to a DAP
- * (DAC outputs are selected by the DAP)
- * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
- * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
- */
-extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
-
-#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
deleted file mode 100644
index e53349912b2..00000000000
--- a/sound/soc/tegra/tegra_i2s.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * tegra_i2s.c - Tegra I2S driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * Copyright (c) 2009-2010, NVIDIA Corporation.
- * Scott Peterson <speterson@nvidia.com>
- *
- * Copyright (C) 2010 Google, Inc.
- * Iliyan Malchev <malchev@google.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <mach/iomap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "tegra_i2s.h"
-
-#define DRV_NAME "tegra-i2s"
-
-static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
-{
- __raw_writel(val, i2s->regs + reg);
-}
-
-static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
-{
- return __raw_readl(i2s->regs + reg);
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_i2s_show(struct seq_file *s, void *unused)
-{
-#define REG(r) { r, #r }
- static const struct {
- int offset;
- const char *name;
- } regs[] = {
- REG(TEGRA_I2S_CTRL),
- REG(TEGRA_I2S_STATUS),
- REG(TEGRA_I2S_TIMING),
- REG(TEGRA_I2S_FIFO_SCR),
- REG(TEGRA_I2S_PCM_CTRL),
- REG(TEGRA_I2S_NW_CTRL),
- REG(TEGRA_I2S_TDM_CTRL),
- REG(TEGRA_I2S_TDM_TX_RX_CTRL),
- };
-#undef REG
-
- struct tegra_i2s *i2s = s->private;
- int i;
-
- clk_enable(i2s->clk_i2s);
-
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- u32 val = tegra_i2s_read(i2s, regs[i].offset);
- seq_printf(s, "%s = %08x\n", regs[i].name, val);
- }
-
- clk_disable(i2s->clk_i2s);
-
- return 0;
-}
-
-static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tegra_i2s_show, inode->i_private);
-}
-
-static const struct file_operations tegra_i2s_debug_fops = {
- .open = tegra_i2s_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void tegra_i2s_debug_add(struct tegra_i2s *i2s)
-{
- i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO,
- snd_soc_debugfs_root, i2s,
- &tegra_i2s_debug_fops);
-}
-
-static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
-{
- if (i2s->debug)
- debugfs_remove(i2s->debug);
-}
-#else
-static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
-{
-}
-
-static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
-{
-}
-#endif
-
-static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
-{
- struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- default:
- return -EINVAL;
- }
-
- i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- break;
- default:
- return -EINVAL;
- }
-
- i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK |
- TEGRA_I2S_CTRL_LRCK_MASK);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
- break;
- case SND_SOC_DAIFMT_I2S:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct device *dev = substream->pcm->card->dev;
- struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- u32 reg;
- int ret, sample_size, srate, i2sclock, bitcnt;
-
- i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
- sample_size = 16;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
- sample_size = 24;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
- sample_size = 32;
- break;
- default:
- return -EINVAL;
- }
-
- srate = params_rate(params);
-
- /* Final "* 2" required by Tegra hardware */
- i2sclock = srate * params_channels(params) * sample_size * 2;
-
- ret = clk_set_rate(i2s->clk_i2s, i2sclock);
- if (ret) {
- dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
- return ret;
- }
-
- bitcnt = (i2sclock / (2 * srate)) - 1;
- if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
- return -EINVAL;
- reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
-
- if (i2sclock % (2 * srate))
- reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
-
- if (!i2s->clk_refs)
- clk_enable(i2s->clk_i2s);
-
- tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
-
- tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
- TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
- TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
-
- if (!i2s->clk_refs)
- clk_disable(i2s->clk_i2s);
-
- return 0;
-}
-
-static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
-{
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
- tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
-{
- i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
- tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
-{
- i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
- tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
-{
- i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
- tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- if (!i2s->clk_refs)
- clk_enable(i2s->clk_i2s);
- i2s->clk_refs++;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- tegra_i2s_start_playback(i2s);
- else
- tegra_i2s_start_capture(i2s);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- tegra_i2s_stop_playback(i2s);
- else
- tegra_i2s_stop_capture(i2s);
- i2s->clk_refs--;
- if (!i2s->clk_refs)
- clk_disable(i2s->clk_i2s);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tegra_i2s_probe(struct snd_soc_dai *dai)
-{
- struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
-
- dai->capture_dma_data = &i2s->capture_dma_data;
- dai->playback_dma_data = &i2s->playback_dma_data;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
- .set_fmt = tegra_i2s_set_fmt,
- .hw_params = tegra_i2s_hw_params,
- .trigger = tegra_i2s_trigger,
-};
-
-static const struct snd_soc_dai_driver tegra_i2s_dai_template = {
- .probe = tegra_i2s_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &tegra_i2s_dai_ops,
- .symmetric_rates = 1,
-};
-
-static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
-{
- struct tegra_i2s * i2s;
- struct resource *mem, *memregion, *dmareq;
- u32 of_dma[2];
- u32 dma_ch;
- int ret;
-
- i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL);
- if (!i2s) {
- dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
- ret = -ENOMEM;
- goto err;
- }
- dev_set_drvdata(&pdev->dev, i2s);
-
- i2s->dai = tegra_i2s_dai_template;
- i2s->dai.name = dev_name(&pdev->dev);
-
- i2s->clk_i2s = clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2s->clk_i2s)) {
- dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
- ret = PTR_ERR(i2s->clk_i2s);
- goto err;
- }
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmareq) {
- if (of_property_read_u32_array(pdev->dev.of_node,
- "nvidia,dma-request-selector",
- of_dma, 2) < 0) {
- dev_err(&pdev->dev, "No DMA resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
- dma_ch = of_dma[1];
- } else {
- dma_ch = dmareq->start;
- }
-
- 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;
- }
-
- i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!i2s->regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err_clk_put;
- }
-
- i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
- i2s->capture_dma_data.wrap = 4;
- i2s->capture_dma_data.width = 32;
- i2s->capture_dma_data.req_sel = dma_ch;
-
- i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
- i2s->playback_dma_data.wrap = 4;
- i2s->playback_dma_data.width = 32;
- i2s->playback_dma_data.req_sel = dma_ch;
-
- i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
-
- ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
- ret = -ENOMEM;
- goto err_clk_put;
- }
-
- tegra_i2s_debug_add(i2s);
-
- return 0;
-
-err_clk_put:
- clk_put(i2s->clk_i2s);
-err:
- return ret;
-}
-
-static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
-{
- struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
-
- snd_soc_unregister_dai(&pdev->dev);
-
- tegra_i2s_debug_remove(i2s);
-
- clk_put(i2s->clk_i2s);
-
- return 0;
-}
-
-static const struct of_device_id tegra_i2s_of_match[] __devinitconst = {
- { .compatible = "nvidia,tegra20-i2s", },
- {},
-};
-
-static struct platform_driver tegra_i2s_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .of_match_table = tegra_i2s_of_match,
- },
- .probe = tegra_i2s_platform_probe,
- .remove = __devexit_p(tegra_i2s_platform_remove),
-};
-module_platform_driver(tegra_i2s_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra I2S ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_DEVICE_TABLE(of, tegra_i2s_of_match);
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
deleted file mode 100644
index 15ce1e2e8bd..00000000000
--- a/sound/soc/tegra/tegra_i2s.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * tegra_i2s.h - Definitions for Tegra I2S driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * Copyright (c) 2009-2010, NVIDIA Corporation.
- * Scott Peterson <speterson@nvidia.com>
- *
- * Copyright (C) 2010 Google, Inc.
- * Iliyan Malchev <malchev@google.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_I2S_H__
-#define __TEGRA_I2S_H__
-
-#include "tegra_pcm.h"
-
-/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
-
-#define TEGRA_I2S_CTRL 0x00
-#define TEGRA_I2S_STATUS 0x04
-#define TEGRA_I2S_TIMING 0x08
-#define TEGRA_I2S_FIFO_SCR 0x0c
-#define TEGRA_I2S_PCM_CTRL 0x10
-#define TEGRA_I2S_NW_CTRL 0x14
-#define TEGRA_I2S_TDM_CTRL 0x20
-#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24
-#define TEGRA_I2S_FIFO1 0x40
-#define TEGRA_I2S_FIFO2 0x80
-
-/* Fields in TEGRA_I2S_CTRL */
-
-#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30)
-#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29)
-#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28)
-#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27)
-#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26)
-#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25)
-
-#define TEGRA_I2S_LRCK_LEFT_LOW 0
-#define TEGRA_I2S_LRCK_RIGHT_LOW 1
-
-#define TEGRA_I2S_CTRL_LRCK_SHIFT 24
-#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT)
-#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
-#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
-
-#define TEGRA_I2S_BIT_FORMAT_I2S 0
-#define TEGRA_I2S_BIT_FORMAT_RJM 1
-#define TEGRA_I2S_BIT_FORMAT_LJM 2
-#define TEGRA_I2S_BIT_FORMAT_DSP 3
-
-#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10
-#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-
-#define TEGRA_I2S_BIT_SIZE_16 0
-#define TEGRA_I2S_BIT_SIZE_20 1
-#define TEGRA_I2S_BIT_SIZE_24 2
-#define TEGRA_I2S_BIT_SIZE_32 3
-
-#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8
-#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-
-#define TEGRA_I2S_FIFO_16_LSB 0
-#define TEGRA_I2S_FIFO_20_LSB 1
-#define TEGRA_I2S_FIFO_24_LSB 2
-#define TEGRA_I2S_FIFO_32 3
-#define TEGRA_I2S_FIFO_PACKED 7
-
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-
-#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3)
-#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2)
-#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1)
-#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0)
-
-/* Fields in TEGRA_I2S_STATUS */
-
-#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31)
-#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30)
-#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29)
-#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28)
-#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3)
-#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2)
-#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1)
-#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0)
-
-/* Fields in TEGRA_I2S_TIMING */
-
-#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
-
-/* Fields in TEGRA_I2S_FIFO_SCR */
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
-#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
-#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8)
-
-#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0
-#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
-#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
-#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-
-struct tegra_i2s {
- struct snd_soc_dai_driver dai;
- struct clk *clk_i2s;
- int clk_refs;
- struct tegra_pcm_dma_params capture_dma_data;
- struct tegra_pcm_dma_params playback_dma_data;
- void __iomem *regs;
- struct dentry *debug;
- u32 reg_ctrl;
-};
-
-#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 8b4457137c7..127348dc09b 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -2,7 +2,7 @@
* tegra_pcm.c - Tegra PCM driver
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
*
* Based on code copyright/by:
*
@@ -29,8 +29,8 @@
*
*/
-#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -39,8 +39,6 @@
#include "tegra_pcm.h"
-#define DRV_NAME "tegra-pcm-audio"
-
static const struct snd_pcm_hardware tegra_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -372,28 +370,18 @@ static struct snd_soc_platform_driver tegra_pcm_platform = {
.pcm_free = tegra_pcm_free,
};
-static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
+int __devinit tegra_pcm_platform_register(struct device *dev)
{
- return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
+ return snd_soc_register_platform(dev, &tegra_pcm_platform);
}
+EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
-static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
+void __devexit tegra_pcm_platform_unregister(struct device *dev)
{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ snd_soc_unregister_platform(dev);
}
-
-static struct platform_driver tegra_pcm_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
- .probe = tegra_pcm_platform_probe,
- .remove = __devexit_p(tegra_pcm_platform_remove),
-};
-module_platform_driver(tegra_pcm_driver);
+EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra PCM ASoC driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index dbb90339fe0..985d418a35e 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -2,7 +2,7 @@
* tegra_pcm.h - Definitions for Tegra PCM driver
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
*
* Based on code copyright/by:
*
@@ -52,4 +52,7 @@ struct tegra_runtime_data {
struct tegra_dma_channel *dma_chan;
};
+int tegra_pcm_platform_register(struct device *dev);
+void tegra_pcm_platform_unregister(struct device *dev);
+
#endif
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c
deleted file mode 100644
index 9ff2c601445..00000000000
--- a/sound/soc/tegra/tegra_spdif.c
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * tegra_spdif.c - Tegra SPDIF driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2011 - NVIDIA, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <mach/iomap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "tegra_spdif.h"
-
-#define DRV_NAME "tegra-spdif"
-
-static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg,
- u32 val)
-{
- __raw_writel(val, spdif->regs + reg);
-}
-
-static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg)
-{
- return __raw_readl(spdif->regs + reg);
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_spdif_show(struct seq_file *s, void *unused)
-{
-#define REG(r) { r, #r }
- static const struct {
- int offset;
- const char *name;
- } regs[] = {
- REG(TEGRA_SPDIF_CTRL),
- REG(TEGRA_SPDIF_STATUS),
- REG(TEGRA_SPDIF_STROBE_CTRL),
- REG(TEGRA_SPDIF_DATA_FIFO_CSR),
- REG(TEGRA_SPDIF_CH_STA_RX_A),
- REG(TEGRA_SPDIF_CH_STA_RX_B),
- REG(TEGRA_SPDIF_CH_STA_RX_C),
- REG(TEGRA_SPDIF_CH_STA_RX_D),
- REG(TEGRA_SPDIF_CH_STA_RX_E),
- REG(TEGRA_SPDIF_CH_STA_RX_F),
- REG(TEGRA_SPDIF_CH_STA_TX_A),
- REG(TEGRA_SPDIF_CH_STA_TX_B),
- REG(TEGRA_SPDIF_CH_STA_TX_C),
- REG(TEGRA_SPDIF_CH_STA_TX_D),
- REG(TEGRA_SPDIF_CH_STA_TX_E),
- REG(TEGRA_SPDIF_CH_STA_TX_F),
- };
-#undef REG
-
- struct tegra_spdif *spdif = s->private;
- int i;
-
- clk_enable(spdif->clk_spdif_out);
-
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- u32 val = tegra_spdif_read(spdif, regs[i].offset);
- seq_printf(s, "%s = %08x\n", regs[i].name, val);
- }
-
- clk_disable(spdif->clk_spdif_out);
-
- return 0;
-}
-
-static int tegra_spdif_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tegra_spdif_show, inode->i_private);
-}
-
-static const struct file_operations tegra_spdif_debug_fops = {
- .open = tegra_spdif_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void tegra_spdif_debug_add(struct tegra_spdif *spdif)
-{
- spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
- snd_soc_debugfs_root, spdif,
- &tegra_spdif_debug_fops);
-}
-
-static void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
-{
- if (spdif->debug)
- debugfs_remove(spdif->debug);
-}
-#else
-static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif)
-{
-}
-
-static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
-{
-}
-#endif
-
-static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct device *dev = substream->pcm->card->dev;
- struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
- int ret, spdifclock;
-
- spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK;
- spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK;
- spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT;
- break;
- default:
- return -EINVAL;
- }
-
- switch (params_rate(params)) {
- case 32000:
- spdifclock = 4096000;
- break;
- case 44100:
- spdifclock = 5644800;
- break;
- case 48000:
- spdifclock = 6144000;
- break;
- case 88200:
- spdifclock = 11289600;
- break;
- case 96000:
- spdifclock = 12288000;
- break;
- case 176400:
- spdifclock = 22579200;
- break;
- case 192000:
- spdifclock = 24576000;
- break;
- default:
- return -EINVAL;
- }
-
- ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
- if (ret) {
- dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static void tegra_spdif_start_playback(struct tegra_spdif *spdif)
-{
- spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN;
- tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
-}
-
-static void tegra_spdif_stop_playback(struct tegra_spdif *spdif)
-{
- spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN;
- tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
-}
-
-static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- if (!spdif->clk_refs)
- clk_enable(spdif->clk_spdif_out);
- spdif->clk_refs++;
- tegra_spdif_start_playback(spdif);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- tegra_spdif_stop_playback(spdif);
- spdif->clk_refs--;
- if (!spdif->clk_refs)
- clk_disable(spdif->clk_spdif_out);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tegra_spdif_probe(struct snd_soc_dai *dai)
-{
- struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
-
- dai->capture_dma_data = NULL;
- dai->playback_dma_data = &spdif->playback_dma_data;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops tegra_spdif_dai_ops = {
- .hw_params = tegra_spdif_hw_params,
- .trigger = tegra_spdif_trigger,
-};
-
-static struct snd_soc_dai_driver tegra_spdif_dai = {
- .name = DRV_NAME,
- .probe = tegra_spdif_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &tegra_spdif_dai_ops,
-};
-
-static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev)
-{
- struct tegra_spdif *spdif;
- struct resource *mem, *memregion, *dmareq;
- int ret;
-
- spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL);
- if (!spdif) {
- dev_err(&pdev->dev, "Can't allocate tegra_spdif\n");
- ret = -ENOMEM;
- goto exit;
- }
- dev_set_drvdata(&pdev->dev, spdif);
-
- spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
- if (IS_ERR(spdif->clk_spdif_out)) {
- pr_err("Can't retrieve spdif clock\n");
- ret = PTR_ERR(spdif->clk_spdif_out);
- goto err_free;
- }
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No memory resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmareq) {
- dev_err(&pdev->dev, "No DMA resource\n");
- ret = -ENODEV;
- goto err_clk_put;
- }
-
- memregion = request_mem_region(mem->start, resource_size(mem),
- DRV_NAME);
- if (!memregion) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- ret = -EBUSY;
- goto err_clk_put;
- }
-
- spdif->regs = ioremap(mem->start, resource_size(mem));
- if (!spdif->regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto err_release;
- }
-
- spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT;
- spdif->playback_dma_data.wrap = 4;
- spdif->playback_dma_data.width = 32;
- spdif->playback_dma_data.req_sel = dmareq->start;
-
- ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
- ret = -ENOMEM;
- goto err_unmap;
- }
-
- tegra_spdif_debug_add(spdif);
-
- return 0;
-
-err_unmap:
- iounmap(spdif->regs);
-err_release:
- release_mem_region(mem->start, resource_size(mem));
-err_clk_put:
- clk_put(spdif->clk_spdif_out);
-err_free:
- kfree(spdif);
-exit:
- return ret;
-}
-
-static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev)
-{
- struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev);
- struct resource *res;
-
- snd_soc_unregister_dai(&pdev->dev);
-
- tegra_spdif_debug_remove(spdif);
-
- iounmap(spdif->regs);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- clk_put(spdif->clk_spdif_out);
-
- kfree(spdif);
-
- return 0;
-}
-
-static struct platform_driver tegra_spdif_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
- .probe = tegra_spdif_platform_probe,
- .remove = __devexit_p(tegra_spdif_platform_remove),
-};
-
-module_platform_driver(tegra_spdif_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra SPDIF ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h
deleted file mode 100644
index 2e03db43027..00000000000
--- a/sound/soc/tegra/tegra_spdif.h
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * tegra_spdif.h - Definitions for Tegra SPDIF driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2011 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- * Copyright (c) 2008-2009, NVIDIA Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_SPDIF_H__
-#define __TEGRA_SPDIF_H__
-
-#include "tegra_pcm.h"
-
-/* Offsets from TEGRA_SPDIF_BASE */
-
-#define TEGRA_SPDIF_CTRL 0x0
-#define TEGRA_SPDIF_STATUS 0x4
-#define TEGRA_SPDIF_STROBE_CTRL 0x8
-#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C
-#define TEGRA_SPDIF_DATA_OUT 0x40
-#define TEGRA_SPDIF_DATA_IN 0x80
-#define TEGRA_SPDIF_CH_STA_RX_A 0x100
-#define TEGRA_SPDIF_CH_STA_RX_B 0x104
-#define TEGRA_SPDIF_CH_STA_RX_C 0x108
-#define TEGRA_SPDIF_CH_STA_RX_D 0x10C
-#define TEGRA_SPDIF_CH_STA_RX_E 0x110
-#define TEGRA_SPDIF_CH_STA_RX_F 0x114
-#define TEGRA_SPDIF_CH_STA_TX_A 0x140
-#define TEGRA_SPDIF_CH_STA_TX_B 0x144
-#define TEGRA_SPDIF_CH_STA_TX_C 0x148
-#define TEGRA_SPDIF_CH_STA_TX_D 0x14C
-#define TEGRA_SPDIF_CH_STA_TX_E 0x150
-#define TEGRA_SPDIF_CH_STA_TX_F 0x154
-#define TEGRA_SPDIF_USR_STA_RX_A 0x180
-#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0
-
-/* Fields in TEGRA_SPDIF_CTRL */
-
-/* Start capturing from 0=right, 1=left channel */
-#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30)
-
-/* SPDIF receiver(RX) enable */
-#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29)
-
-/* SPDIF Transmitter(TX) enable */
-#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28)
-
-/* Transmit Channel status */
-#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27)
-
-/* Transmit user Data */
-#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26)
-
-/* Interrupt on transmit error */
-#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25)
-
-/* Interrupt on receive error */
-#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24)
-
-/* Interrupt on invalid preamble */
-#define TEGRA_SPDIF_CTRL_IE_P (1 << 23)
-
-/* Interrupt on "B" preamble */
-#define TEGRA_SPDIF_CTRL_IE_B (1 << 22)
-
-/* Interrupt when block of channel status received */
-#define TEGRA_SPDIF_CTRL_IE_C (1 << 21)
-
-/* Interrupt when a valid information unit (IU) is received */
-#define TEGRA_SPDIF_CTRL_IE_U (1 << 20)
-
-/* Interrupt when RX user FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19)
-
-/* Interrupt when TX user FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18)
-
-/* Interrupt when RX data FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17)
-
-/* Interrupt when TX data FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16)
-
-/* Loopback test mode enable */
-#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15)
-
-/*
- * Pack data mode:
- * 0 = Single data (16 bit needs to be padded to match the
- * interface data bit size).
- * 1 = Packeted left/right channel data into a single word.
- */
-#define TEGRA_SPDIF_CTRL_PACK (1 << 14)
-
-/*
- * 00 = 16bit data
- * 01 = 20bit data
- * 10 = 24bit data
- * 11 = raw data
- */
-#define TEGRA_SPDIF_BIT_MODE_16BIT 0
-#define TEGRA_SPDIF_BIT_MODE_20BIT 1
-#define TEGRA_SPDIF_BIT_MODE_24BIT 2
-#define TEGRA_SPDIF_BIT_MODE_RAW 3
-
-#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12
-#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-
-/* Fields in TEGRA_SPDIF_STATUS */
-
-/*
- * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
- * write a 1 to the corresponding bit location to clear the status.
- */
-
-/*
- * Receiver(RX) shifter is busy receiving data.
- * This bit is asserted when the receiver first locked onto the
- * preamble of the data stream after RX_EN is asserted. This bit is
- * deasserted when either,
- * (a) the end of a frame is reached after RX_EN is deeasserted, or
- * (b) the SPDIF data stream becomes inactive.
- */
-#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29)
-
-/*
- * Transmitter(TX) shifter is busy transmitting data.
- * This bit is asserted when TX_EN is asserted.
- * This bit is deasserted when the end of a frame is reached after
- * TX_EN is deasserted.
- */
-#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28)
-
-/*
- * TX is busy shifting out channel status.
- * This bit is asserted when both TX_EN and TC_EN are asserted and
- * data from CH_STA_TX_A register is loaded into the internal shifter.
- * This bit is deasserted when either,
- * (a) the end of a frame is reached after TX_EN is deasserted, or
- * (b) CH_STA_TX_F register is loaded into the internal shifter.
- */
-#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27)
-
-/*
- * TX User data FIFO busy.
- * This bit is asserted when TX_EN and TXU_EN are asserted and
- * there's data in the TX user FIFO. This bit is deassert when either,
- * (a) the end of a frame is reached after TX_EN is deasserted, or
- * (b) there's no data left in the TX user FIFO.
- */
-#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26)
-
-/* TX FIFO Underrun error status */
-#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25)
-
-/* RX FIFO Overrun error status */
-#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24)
-
-/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
-#define TEGRA_SPDIF_STATUS_IS_P (1 << 23)
-
-/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
-#define TEGRA_SPDIF_STATUS_IS_B (1 << 22)
-
-/*
- * RX channel block data receive status:
- * 0=entire block not recieved yet.
- * 1=received entire block of channel status,
- */
-#define TEGRA_SPDIF_STATUS_IS_C (1 << 21)
-
-/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
-#define TEGRA_SPDIF_STATUS_IS_U (1 << 20)
-
-/*
- * RX User FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19)
-
-/*
- * TX User FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18)
-
-/*
- * RX Data FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17)
-
-/*
- * TX Data FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16)
-
-/* Fields in TEGRA_SPDIF_STROBE_CTRL */
-
-/*
- * Indicates the approximate number of detected SPDIFIN clocks within a
- * bi-phase period.
- */
-#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16
-#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
-
-/* Data strobe mode: 0=Auto-locked 1=Manual locked */
-#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15)
-
-/*
- * Manual data strobe time within the bi-phase clock period (in terms of
- * the number of over-sampling clocks).
- */
-#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8
-#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
-
-/*
- * Manual SPDIFIN bi-phase clock period (in terms of the number of
- * over-sampling clocks).
- */
-#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0
-#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
-
-/* Fields in SPDIF_DATA_FIFO_CSR */
-
-/* Clear Receiver User FIFO (RX USR.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31)
-
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3
-
-/* RU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \
- (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-
-/* Number of RX USR.FIFO levels with valid data. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
-
-/* Clear Transmitter User FIFO (TX USR.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23)
-
-/* TU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \
- (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-
-/* Number of TX USR.FIFO levels that could be filled. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
-
-/* Clear Receiver Data FIFO (RX DATA.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15)
-
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3
-
-/* RU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \
- (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-
-/* Number of RX DATA.FIFO levels with valid data. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
-
-/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7)
-
-/* TU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \
- (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \
- (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-
-/* Number of TX DATA.FIFO levels that could be filled. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_DATA_OUT */
-
-/*
- * This register has 5 different formats:
- * 16-bit (BIT_MODE=00, PACK=0)
- * 20-bit (BIT_MODE=01, PACK=0)
- * 24-bit (BIT_MODE=10, PACK=0)
- * raw (BIT_MODE=11, PACK=0)
- * 16-bit packed (BIT_MODE=00, PACK=1)
- */
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0
-#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0
-#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_DATA_IN */
-
-/*
- * This register has 5 different formats:
- * 16-bit (BIT_MODE=00, PACK=0)
- * 20-bit (BIT_MODE=01, PACK=0)
- * 24-bit (BIT_MODE=10, PACK=0)
- * raw (BIT_MODE=11, PACK=0)
- * 16-bit packed (BIT_MODE=00, PACK=1)
- *
- * Bits 31:24 are common to all modes except 16-bit packed
- */
-
-#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31)
-#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30)
-#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29)
-#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24
-#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0
-#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0
-#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0
-#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_CH_STA_RX_A */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_B */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_C */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_D */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_E */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_F */
-
-/*
- * The 6-word receive channel data page buffer holds a block (192 frames) of
- * channel status information. The order of receive is from LSB to MSB
- * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
- */
-
-/* Fields in TEGRA_SPDIF_CH_STA_TX_A */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_B */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_C */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_D */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_E */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_F */
-
-/*
- * The 6-word transmit channel data page buffer holds a block (192 frames) of
- * channel status information. The order of transmission is from LSB to MSB
- * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
- */
-
-/* Fields in TEGRA_SPDIF_USR_STA_RX_A */
-
-/*
- * This 4-word deep FIFO receives user FIFO field information. The order of
- * receive is from LSB to MSB bit.
- */
-
-/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */
-
-/*
- * This 4-word deep FIFO transmits user FIFO field information. The order of
- * transmission is from LSB to MSB bit.
- */
-
-struct tegra_spdif {
- struct clk *clk_spdif_out;
- int clk_refs;
- struct tegra_pcm_dma_params capture_dma_data;
- struct tegra_pcm_dma_params playback_dma_data;
- void __iomem *regs;
- struct dentry *debug;
- u32 reg_ctrl;
-};
-
-#endif
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
new file mode 100644
index 00000000000..4e77026807a
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -0,0 +1,224 @@
+/*
+ * tegra_wm8753.c - Tegra machine ASoC driver for boards using WM8753 codec.
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <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/wm8753.h"
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-wm8753"
+
+struct tegra_wm8753 {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int tegra_wm8753_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 = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12288000;
+ break;
+ }
+
+ 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, WM8753_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_wm8753_ops = {
+ .hw_params = tegra_wm8753_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tegra_wm8753_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static struct snd_soc_dai_link tegra_wm8753_dai = {
+ .name = "WM8753",
+ .stream_name = "WM8753 PCM",
+ .codec_dai_name = "wm8753-hifi",
+ .ops = &tegra_wm8753_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_wm8753 = {
+ .name = "tegra-wm8753",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_wm8753_dai,
+ .num_links = 1,
+
+ .dapm_widgets = tegra_wm8753_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_wm8753_dapm_widgets),
+ .fully_routed = true,
+};
+
+static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_wm8753;
+ struct tegra_wm8753 *machine;
+ int ret;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753),
+ GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ 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_wm8753_dai.codec_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,audio-codec", 0);
+ if (!tegra_wm8753_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,i2s-controller", 0);
+ if (!tegra_wm8753_dai.cpu_dai_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_wm8753_dai.platform_of_node =
+ tegra_wm8753_dai.cpu_dai_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 __devexit tegra_wm8753_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_wm8753_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra-audio-wm8753", },
+ {},
+};
+
+static struct platform_driver tegra_wm8753_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_wm8753_of_match,
+ },
+ .probe = tegra_wm8753_driver_probe,
+ .remove = __devexit_p(tegra_wm8753_driver_remove),
+};
+module_platform_driver(tegra_wm8753_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+WM8753 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm8753_of_match);
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 566655e23b7..0b0df49d9d3 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -2,7 +2,7 @@
* tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
*
* Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
*
* Based on code copyright/by:
*
@@ -46,9 +46,6 @@
#include "../codecs/wm8903.h"
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-wm8903"
@@ -61,7 +58,6 @@
struct tegra_wm8903 {
struct tegra_wm8903_platform_data pdata;
- struct platform_device *pcm_dev;
struct tegra_asoc_utils_data util_data;
int gpio_requested;
};
@@ -354,8 +350,8 @@ static struct snd_soc_dai_link tegra_wm8903_dai = {
.name = "WM8903",
.stream_name = "WM8903 PCM",
.codec_name = "wm8903.0-001a",
- .platform_name = "tegra-pcm-audio",
- .cpu_dai_name = "tegra-i2s.0",
+ .platform_name = "tegra20-i2s.0",
+ .cpu_dai_name = "tegra20-i2s.0",
.codec_dai_name = "wm8903-hifi",
.init = tegra_wm8903_init,
.ops = &tegra_wm8903_ops,
@@ -392,7 +388,6 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err;
}
- machine->pcm_dev = ERR_PTR(-EINVAL);
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
@@ -428,14 +423,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
goto err;
}
- machine->pcm_dev = platform_device_register_simple(
- "tegra-pcm-audio", -1, NULL, 0);
- if (IS_ERR(machine->pcm_dev)) {
- dev_err(&pdev->dev,
- "Can't instantiate tegra-pcm-audio\n");
- ret = PTR_ERR(machine->pcm_dev);
- goto err;
- }
+ tegra_wm8903_dai.platform_name = NULL;
+ tegra_wm8903_dai.platform_of_node =
+ tegra_wm8903_dai.cpu_dai_of_node;
} else {
if (machine_is_harmony()) {
card->dapm_routes = harmony_audio_map;
@@ -454,7 +444,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
if (ret)
- goto err_unregister;
+ goto err;
ret = snd_soc_register_card(card);
if (ret) {
@@ -467,9 +457,6 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
err_fini_utils:
tegra_asoc_utils_fini(&machine->util_data);
-err_unregister:
- if (!IS_ERR(machine->pcm_dev))
- platform_device_unregister(machine->pcm_dev);
err:
return ret;
}
@@ -497,8 +484,6 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);
- if (!IS_ERR(machine->pcm_dev))
- platform_device_unregister(machine->pcm_dev);
return 0;
}
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 2bdfc550cff..4a8d5b672c9 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -27,6 +27,7 @@
#include <asm/mach-types.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -38,9 +39,6 @@
#include "../codecs/tlv320aic23.h"
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-trimslice"
@@ -119,8 +117,8 @@ static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
.name = "TLV320AIC23",
.stream_name = "AIC23",
.codec_name = "tlv320aic23-codec.2-001a",
- .platform_name = "tegra-pcm-audio",
- .cpu_dai_name = "tegra-i2s.0",
+ .platform_name = "tegra20-i2s.0",
+ .cpu_dai_name = "tegra20-i2s.0",
.codec_dai_name = "tlv320aic23-hifi",
.ops = &trimslice_asoc_ops,
};
@@ -152,6 +150,32 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
goto err;
}
+ if (pdev->dev.of_node) {
+ trimslice_tlv320aic23_dai.codec_name = NULL;
+ trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,audio-codec", 0);
+ if (!trimslice_tlv320aic23_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ trimslice_tlv320aic23_dai.cpu_dai_name = NULL;
+ trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle(
+ pdev->dev.of_node, "nvidia,i2s-controller", 0);
+ if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ trimslice_tlv320aic23_dai.platform_name = NULL;
+ trimslice_tlv320aic23_dai.platform_of_node =
+ trimslice_tlv320aic23_dai.cpu_dai_of_node;
+ }
+
ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
if (ret)
goto err;
@@ -187,10 +211,17 @@ static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id trimslice_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra-audio-trimslice", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, trimslice_of_match);
+
static struct platform_driver tegra_snd_trimslice_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = trimslice_of_match,
},
.probe = tegra_snd_trimslice_probe,
.remove = __devexit_p(tegra_snd_trimslice_remove),
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
new file mode 100644
index 00000000000..44cf43404cd
--- /dev/null
+++ b/sound/soc/ux500/Kconfig
@@ -0,0 +1,14 @@
+#
+# Ux500 SoC audio configuration
+#
+menuconfig SND_SOC_UX500
+ tristate "SoC Audio support for Ux500 platform"
+ depends on SND_SOC
+ depends on MFD_DB8500_PRCMU
+ help
+ Say Y if you want to enable ASoC-support for
+ any of the Ux500 platforms (e.g. U8500).
+
+config SND_SOC_UX500_PLAT_MSP_I2S
+ tristate
+ depends on SND_SOC_UX500
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
new file mode 100644
index 00000000000..19974c5a2ea
--- /dev/null
+++ b/sound/soc/ux500/Makefile
@@ -0,0 +1,4 @@
+# Ux500 Platform Support
+
+snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o
+obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
new file mode 100644
index 00000000000..93c6c40e724
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/slab.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "ux500_msp_i2s.h"
+#include "ux500_msp_dai.h"
+
+static int setup_pcm_multichan(struct snd_soc_dai *dai,
+ struct ux500_msp_config *msp_config)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+ struct msp_multichannel_config *multi =
+ &msp_config->multichannel_config;
+
+ if (drvdata->slots > 1) {
+ msp_config->multichannel_configured = 1;
+
+ multi->tx_multichannel_enable = true;
+ multi->rx_multichannel_enable = true;
+ multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
+
+ multi->tx_channel_0_enable = drvdata->tx_mask;
+ multi->tx_channel_1_enable = 0;
+ multi->tx_channel_2_enable = 0;
+ multi->tx_channel_3_enable = 0;
+
+ multi->rx_channel_0_enable = drvdata->rx_mask;
+ multi->rx_channel_1_enable = 0;
+ multi->rx_channel_2_enable = 0;
+ multi->rx_channel_3_enable = 0;
+
+ dev_dbg(dai->dev,
+ "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n",
+ __func__, drvdata->slots, multi->tx_channel_0_enable,
+ multi->rx_channel_0_enable);
+ }
+
+ return 0;
+}
+
+static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
+ struct msp_protdesc *prot_desc)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ switch (drvdata->slots) {
+ case 1:
+ switch (rate) {
+ case 8000:
+ prot_desc->frame_period =
+ FRAME_PER_SINGLE_SLOT_8_KHZ;
+ break;
+
+ case 16000:
+ prot_desc->frame_period =
+ FRAME_PER_SINGLE_SLOT_16_KHZ;
+ break;
+
+ case 44100:
+ prot_desc->frame_period =
+ FRAME_PER_SINGLE_SLOT_44_1_KHZ;
+ break;
+
+ case 48000:
+ prot_desc->frame_period =
+ FRAME_PER_SINGLE_SLOT_48_KHZ;
+ break;
+
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported sample-rate (freq = %d)!\n",
+ __func__, rate);
+ return -EINVAL;
+ }
+ break;
+
+ case 2:
+ prot_desc->frame_period = FRAME_PER_2_SLOTS;
+ break;
+
+ case 8:
+ prot_desc->frame_period = FRAME_PER_8_SLOTS;
+ break;
+
+ case 16:
+ prot_desc->frame_period = FRAME_PER_16_SLOTS;
+ break;
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported slot-count (slots = %d)!\n",
+ __func__, drvdata->slots);
+ return -EINVAL;
+ }
+
+ prot_desc->clocks_per_frame =
+ prot_desc->frame_period+1;
+
+ dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
+ __func__,
+ prot_desc->clocks_per_frame);
+
+ return 0;
+}
+
+static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
+ struct msp_protdesc *prot_desc)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ u32 frame_length = MSP_FRAME_LEN_1;
+ prot_desc->frame_width = 0;
+
+ switch (drvdata->slots) {
+ case 1:
+ frame_length = MSP_FRAME_LEN_1;
+ break;
+
+ case 2:
+ frame_length = MSP_FRAME_LEN_2;
+ break;
+
+ case 8:
+ frame_length = MSP_FRAME_LEN_8;
+ break;
+
+ case 16:
+ frame_length = MSP_FRAME_LEN_16;
+ break;
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported slot-count (slots = %d)!\n",
+ __func__, drvdata->slots);
+ return -EINVAL;
+ }
+
+ prot_desc->tx_frame_len_1 = frame_length;
+ prot_desc->rx_frame_len_1 = frame_length;
+ prot_desc->tx_frame_len_2 = frame_length;
+ prot_desc->rx_frame_len_2 = frame_length;
+
+ prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+ prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+ prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+ prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+
+ return setup_frameper(dai, rate, prot_desc);
+}
+
+static int setup_clocking(struct snd_soc_dai *dai,
+ unsigned int fmt,
+ struct ux500_msp_config *msp_config)
+{
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
+ msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
+
+ break;
+
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
+ __func__, fmt);
+
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dev_dbg(dai->dev, "%s: Codec is master.\n", __func__);
+
+ msp_config->iodelay = 0x20;
+ msp_config->rx_fsync_sel = 0;
+ msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT;
+ msp_config->tx_clk_sel = 0;
+ msp_config->rx_clk_sel = 0;
+ msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT;
+
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__);
+
+ msp_config->tx_clk_sel = TX_CLK_SEL_SRG;
+ msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG;
+ msp_config->rx_clk_sel = RX_CLK_SEL_SRG;
+ msp_config->rx_fsync_sel = RX_SYNC_SRG;
+ msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT;
+
+ break;
+
+ default:
+ dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n",
+ __func__, fmt);
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int setup_pcm_protdesc(struct snd_soc_dai *dai,
+ unsigned int fmt,
+ struct msp_protdesc *prot_desc)
+{
+ prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
+ prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
+ prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+ prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+ prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI);
+ prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT;
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) {
+ dev_dbg(dai->dev, "%s: DSP_A.\n", __func__);
+ prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+ prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+ prot_desc->rx_data_delay = MSP_DELAY_1;
+ prot_desc->tx_data_delay = MSP_DELAY_1;
+ } else {
+ dev_dbg(dai->dev, "%s: DSP_B.\n", __func__);
+ prot_desc->rx_clk_pol = MSP_FALLING_EDGE;
+ prot_desc->tx_clk_pol = MSP_RISING_EDGE;
+
+ prot_desc->rx_data_delay = MSP_DELAY_0;
+ prot_desc->tx_data_delay = MSP_DELAY_0;
+ }
+
+ prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+ prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+ prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+ prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+ prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+ return 0;
+}
+
+static int setup_i2s_protdesc(struct msp_protdesc *prot_desc)
+{
+ prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
+ prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
+ prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+ prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+ prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO);
+ prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT;
+
+ prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1;
+ prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1;
+ prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1;
+ prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1;
+ prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+ prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+ prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+ prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+
+ prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+ prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+ prot_desc->rx_data_delay = MSP_DELAY_0;
+ prot_desc->tx_data_delay = MSP_DELAY_0;
+
+ prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+ prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+ prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+ prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+ prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+ return 0;
+}
+
+static int setup_msp_config(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ struct ux500_msp_config *msp_config)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+ struct msp_protdesc *prot_desc = &msp_config->protdesc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int fmt = drvdata->fmt;
+ int ret;
+
+ memset(msp_config, 0, sizeof(*msp_config));
+
+ msp_config->f_inputclk = drvdata->master_clk;
+
+ msp_config->tx_fifo_config = TX_FIFO_ENABLE;
+ msp_config->rx_fifo_config = RX_FIFO_ENABLE;
+ msp_config->def_elem_len = 1;
+ msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ MSP_DIR_TX : MSP_DIR_RX;
+ msp_config->data_size = MSP_DATA_BITS_32;
+ msp_config->frame_freq = runtime->rate;
+
+ dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n",
+ __func__, msp_config->f_inputclk, msp_config->frame_freq);
+ /* To avoid division by zero */
+ prot_desc->clocks_per_frame = 1;
+
+ dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__,
+ runtime->rate, runtime->channels);
+ switch (fmt &
+ (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+ msp_config->default_protdesc = 1;
+ msp_config->protocol = MSP_I2S_PROTOCOL;
+ break;
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+ msp_config->data_size = MSP_DATA_BITS_16;
+ msp_config->protocol = MSP_I2S_PROTOCOL;
+
+ ret = setup_i2s_protdesc(prot_desc);
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+ dev_dbg(dai->dev, "%s: PCM format.\n", __func__);
+
+ msp_config->data_size = MSP_DATA_BITS_16;
+ msp_config->protocol = MSP_PCM_PROTOCOL;
+
+ ret = setup_pcm_protdesc(dai, fmt, prot_desc);
+ if (ret < 0)
+ return ret;
+
+ ret = setup_pcm_multichan(dai, msp_config);
+ if (ret < 0)
+ return ret;
+
+ ret = setup_pcm_framing(dai, runtime->rate, prot_desc);
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ default:
+ dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ return setup_clocking(dai, fmt, msp_config);
+}
+
+static int ux500_msp_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+ snd_pcm_stream_str(substream));
+
+ /* Enable regulator */
+ ret = regulator_enable(drvdata->reg_vape);
+ if (ret != 0) {
+ dev_err(drvdata->msp->dev,
+ "%s: Failed to enable regulator!\n", __func__);
+ return ret;
+ }
+
+ /* Enable clock */
+ dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__);
+ clk_enable(drvdata->clk);
+
+ return 0;
+}
+
+static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+ snd_pcm_stream_str(substream));
+
+ if (drvdata->vape_opp_constraint == 1) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "ux500_msp_i2s", 50);
+ drvdata->vape_opp_constraint = 0;
+ }
+
+ if (ux500_msp_i2s_close(drvdata->msp,
+ is_playback ? MSP_DIR_TX : MSP_DIR_RX)) {
+ dev_err(dai->dev,
+ "%s: Error: MSP %d (%s): Unable to close i2s.\n",
+ __func__, dai->id, snd_pcm_stream_str(substream));
+ }
+
+ /* Disable clock */
+ clk_disable(drvdata->clk);
+
+ /* Disable regulator */
+ ret = regulator_disable(drvdata->reg_vape);
+ if (ret < 0)
+ dev_err(dai->dev,
+ "%s: ERROR: Failed to disable regulator (%d)!\n",
+ __func__, ret);
+}
+
+static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct ux500_msp_config msp_config;
+
+ dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__,
+ dai->id, snd_pcm_stream_str(substream), runtime->rate);
+
+ setup_msp_config(substream, dai, &msp_config);
+
+ ret = ux500_msp_i2s_open(drvdata->msp, &msp_config);
+ if (ret < 0) {
+ dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Set OPP-level */
+ if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
+ (drvdata->msp->f_bitclk > 19200000)) {
+ /* If the bit-clock is higher than 19.2MHz, Vape should be
+ * run in 100% OPP. Only when bit-clock is used (MSP master) */
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "ux500-msp-i2s", 100);
+ drvdata->vape_opp_constraint = 1;
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "ux500-msp-i2s", 50);
+ drvdata->vape_opp_constraint = 0;
+ }
+
+ return ret;
+}
+
+static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned int mask, slots_active;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
+ __func__, dai->id, snd_pcm_stream_str(substream));
+
+ switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 2);
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_DSP_A:
+ mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ drvdata->tx_mask :
+ drvdata->rx_mask;
+
+ slots_active = hweight32(mask);
+ dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
+
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ slots_active, slots_active);
+ break;
+
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported protocol (fmt = 0x%x)!\n",
+ __func__, drvdata->fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id);
+
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ break;
+
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
+ __func__, drvdata->fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+
+ default:
+ dev_err(dai->dev,
+ "%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
+ __func__, drvdata->fmt);
+ return -EINVAL;
+ }
+
+ drvdata->fmt = fmt;
+ return 0;
+}
+
+static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+ unsigned int cap;
+
+ switch (slots) {
+ case 1:
+ cap = 0x01;
+ break;
+ case 2:
+ cap = 0x03;
+ break;
+ case 8:
+ cap = 0xFF;
+ break;
+ case 16:
+ cap = 0xFFFF;
+ break;
+ default:
+ dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+ drvdata->slots = slots;
+
+ if (!(slot_width == 16)) {
+ dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+ drvdata->slot_width = slot_width;
+
+ drvdata->tx_mask = tx_mask & cap;
+ drvdata->rx_mask = rx_mask & cap;
+
+ return 0;
+}
+
+static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n",
+ __func__, dai->id, clk_id, freq);
+
+ switch (clk_id) {
+ case UX500_MSP_MASTER_CLOCK:
+ drvdata->master_clk = freq;
+ break;
+
+ default:
+ dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n",
+ __func__, dai->id, clk_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n",
+ __func__, dai->id, snd_pcm_stream_str(substream),
+ (int)drvdata->msp->id, cmd);
+
+ ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream);
+
+ return ret;
+}
+
+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->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;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
+ {
+ .set_sysclk = ux500_msp_dai_set_dai_sysclk,
+ .set_fmt = ux500_msp_dai_set_dai_fmt,
+ .set_tdm_slot = ux500_msp_dai_set_tdm_slot,
+ .startup = ux500_msp_dai_startup,
+ .shutdown = ux500_msp_dai_shutdown,
+ .prepare = ux500_msp_dai_prepare,
+ .trigger = ux500_msp_dai_trigger,
+ .hw_params = ux500_msp_dai_hw_params,
+ }
+};
+
+static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
+ {
+ .name = "ux500-msp-i2s.0",
+ .probe = ux500_msp_dai_probe,
+ .id = 0,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = ux500_msp_dai_ops,
+ },
+ {
+ .name = "ux500-msp-i2s.1",
+ .probe = ux500_msp_dai_probe,
+ .id = 1,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = ux500_msp_dai_ops,
+ },
+ {
+ .name = "ux500-msp-i2s.2",
+ .id = 2,
+ .probe = ux500_msp_dai_probe,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = ux500_msp_dai_ops,
+ },
+ {
+ .name = "ux500-msp-i2s.3",
+ .probe = ux500_msp_dai_probe,
+ .id = 3,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = ux500_msp_dai_ops,
+ },
+};
+
+static int __devinit ux500_msp_drv_probe(struct platform_device *pdev)
+{
+ struct ux500_msp_i2s_drvdata *drvdata;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
+ pdev->name);
+
+ drvdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct ux500_msp_i2s_drvdata),
+ GFP_KERNEL);
+ drvdata->fmt = 0;
+ drvdata->slots = 1;
+ drvdata->tx_mask = 0x01;
+ drvdata->rx_mask = 0x01;
+ drvdata->slot_width = 16;
+ drvdata->master_clk = MSP_INPUT_FREQ_APB;
+
+ drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape");
+ if (IS_ERR(drvdata->reg_vape)) {
+ ret = (int)PTR_ERR(drvdata->reg_vape);
+ dev_err(&pdev->dev,
+ "%s: ERROR: Failed to get Vape supply (%d)!\n",
+ __func__, ret);
+ return ret;
+ }
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
+
+ drvdata->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(drvdata->clk)) {
+ ret = (int)PTR_ERR(drvdata->clk);
+ dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n",
+ __func__, ret);
+ goto err_clk;
+ }
+
+ ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
+ pdev->dev.platform_data);
+ if (!drvdata->msp) {
+ dev_err(&pdev->dev,
+ "%s: ERROR: Failed to init MSP-struct (%d)!",
+ __func__, ret);
+ goto err_init_msp;
+ }
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ ret = snd_soc_register_dai(&pdev->dev,
+ &ux500_msp_dai_drv[drvdata->msp->id]);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
+ __func__, drvdata->msp->id);
+ goto err_init_msp;
+ }
+
+ return 0;
+
+err_init_msp:
+ clk_put(drvdata->clk);
+
+err_clk:
+ devm_regulator_put(drvdata->reg_vape);
+
+ return ret;
+}
+
+static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)
+{
+ struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+
+ devm_regulator_put(drvdata->reg_vape);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
+
+ clk_put(drvdata->clk);
+
+ ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
+
+ return 0;
+}
+
+static struct platform_driver msp_i2s_driver = {
+ .driver = {
+ .name = "ux500-msp-i2s",
+ .owner = THIS_MODULE,
+ },
+ .probe = ux500_msp_drv_probe,
+ .remove = ux500_msp_drv_remove,
+};
+module_platform_driver(msp_i2s_driver);
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
new file mode 100644
index 00000000000..98202a34a5d
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_msp_dai_H
+#define UX500_msp_dai_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include "ux500_msp_i2s.h"
+
+#define UX500_NBR_OF_DAI 4
+
+#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define FRAME_PER_SINGLE_SLOT_8_KHZ 31
+#define FRAME_PER_SINGLE_SLOT_16_KHZ 124
+#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63
+#define FRAME_PER_SINGLE_SLOT_48_KHZ 49
+#define FRAME_PER_2_SLOTS 31
+#define FRAME_PER_8_SLOTS 138
+#define FRAME_PER_16_SLOTS 277
+
+#ifndef CONFIG_SND_SOC_UX500_AB5500
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
+#else
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2)
+#endif
+
+#define UX500_MSP_MIN_CHANNELS 1
+#define UX500_MSP_MAX_CHANNELS 8
+
+#define PLAYBACK_CONFIGURED 1
+#define CAPTURE_CONFIGURED 2
+
+enum ux500_msp_clock_id {
+ UX500_MSP_MASTER_CLOCK,
+};
+
+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;
+ struct clk *clk;
+
+ /* Regulators */
+ int vape_opp_constraint;
+};
+
+int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
+
+#endif
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
new file mode 100644
index 00000000000..496dec10c96
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * Sandeep Kaushik <sandeep.kaushik@st.com>
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+
+#include "ux500_msp_i2s.h"
+
+ /* Protocol desciptors */
+static const struct msp_protdesc prot_descs[] = {
+ { /* I2S */
+ MSP_SINGLE_PHASE,
+ MSP_SINGLE_PHASE,
+ MSP_PHASE2_START_MODE_IMEDIATE,
+ MSP_PHASE2_START_MODE_IMEDIATE,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_ELEM_LEN_32,
+ MSP_ELEM_LEN_32,
+ MSP_ELEM_LEN_32,
+ MSP_ELEM_LEN_32,
+ MSP_DELAY_1,
+ MSP_DELAY_1,
+ MSP_RISING_EDGE,
+ MSP_FALLING_EDGE,
+ MSP_FSYNC_POL_ACT_LO,
+ MSP_FSYNC_POL_ACT_LO,
+ MSP_SWAP_NONE,
+ MSP_SWAP_NONE,
+ MSP_COMPRESS_MODE_LINEAR,
+ MSP_EXPAND_MODE_LINEAR,
+ MSP_FSYNC_IGNORE,
+ 31,
+ 15,
+ 32,
+ }, { /* PCM */
+ MSP_DUAL_PHASE,
+ MSP_DUAL_PHASE,
+ MSP_PHASE2_START_MODE_FSYNC,
+ MSP_PHASE2_START_MODE_FSYNC,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_ELEM_LEN_16,
+ MSP_ELEM_LEN_16,
+ MSP_ELEM_LEN_16,
+ MSP_ELEM_LEN_16,
+ MSP_DELAY_0,
+ MSP_DELAY_0,
+ MSP_RISING_EDGE,
+ MSP_FALLING_EDGE,
+ MSP_FSYNC_POL_ACT_HI,
+ MSP_FSYNC_POL_ACT_HI,
+ MSP_SWAP_NONE,
+ MSP_SWAP_NONE,
+ MSP_COMPRESS_MODE_LINEAR,
+ MSP_EXPAND_MODE_LINEAR,
+ MSP_FSYNC_IGNORE,
+ 255,
+ 0,
+ 256,
+ }, { /* Companded PCM */
+ MSP_SINGLE_PHASE,
+ MSP_SINGLE_PHASE,
+ MSP_PHASE2_START_MODE_FSYNC,
+ MSP_PHASE2_START_MODE_FSYNC,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_BTF_MS_BIT_FIRST,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_FRAME_LEN_1,
+ MSP_ELEM_LEN_8,
+ MSP_ELEM_LEN_8,
+ MSP_ELEM_LEN_8,
+ MSP_ELEM_LEN_8,
+ MSP_DELAY_0,
+ MSP_DELAY_0,
+ MSP_RISING_EDGE,
+ MSP_RISING_EDGE,
+ MSP_FSYNC_POL_ACT_HI,
+ MSP_FSYNC_POL_ACT_HI,
+ MSP_SWAP_NONE,
+ MSP_SWAP_NONE,
+ MSP_COMPRESS_MODE_LINEAR,
+ MSP_EXPAND_MODE_LINEAR,
+ MSP_FSYNC_IGNORE,
+ 255,
+ 0,
+ 256,
+ },
+};
+
+static void set_prot_desc_tx(struct ux500_msp *msp,
+ struct msp_protdesc *protdesc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2);
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ }
+ temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order);
+ temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode);
+ temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_TCF);
+}
+
+static void set_prot_desc_rx(struct ux500_msp *msp,
+ struct msp_protdesc *protdesc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2);
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ }
+
+ temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order);
+ temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode);
+ temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_RCF);
+}
+
+static int configure_protocol(struct ux500_msp *msp,
+ struct ux500_msp_config *config)
+{
+ struct msp_protdesc *protdesc;
+ enum msp_data_size data_size;
+ u32 temp_reg = 0;
+
+ data_size = config->data_size;
+ msp->def_elem_len = config->def_elem_len;
+ if (config->default_protdesc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n",
+ __func__);
+ return -EINVAL;
+ }
+ protdesc =
+ (struct msp_protdesc *)&prot_descs[config->protocol];
+ } else {
+ protdesc = (struct msp_protdesc *)&config->protdesc;
+ }
+
+ if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
+ dev_err(msp->dev,
+ "%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
+ __func__, data_size);
+ return -EINVAL;
+ }
+
+ if (config->direction & MSP_DIR_TX)
+ set_prot_desc_tx(msp, protdesc, data_size);
+ if (config->direction & MSP_DIR_RX)
+ set_prot_desc_rx(msp, protdesc, data_size);
+
+ /* The code below should not be separated. */
+ temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+ temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+ temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+ temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+ u32 reg_val_GCR;
+ u32 frame_per = 0;
+ u32 sck_div = 0;
+ u32 frame_width = 0;
+ u32 temp_reg = 0;
+ struct msp_protdesc *protdesc = NULL;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
+
+ if (config->default_protdesc)
+ protdesc =
+ (struct msp_protdesc *)&prot_descs[config->protocol];
+ else
+ protdesc = (struct msp_protdesc *)&config->protdesc;
+
+ switch (config->protocol) {
+ case MSP_PCM_PROTOCOL:
+ case MSP_PCM_COMPAND_PROTOCOL:
+ frame_width = protdesc->frame_width;
+ sck_div = config->f_inputclk / (config->frame_freq *
+ (protdesc->clocks_per_frame));
+ frame_per = protdesc->frame_period;
+ break;
+ case MSP_I2S_PROTOCOL:
+ frame_width = protdesc->frame_width;
+ sck_div = config->f_inputclk / (config->frame_freq *
+ (protdesc->clocks_per_frame));
+ frame_per = protdesc->frame_period;
+ break;
+ default:
+ dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n",
+ __func__,
+ config->protocol);
+ return -EINVAL;
+ }
+
+ temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+ temp_reg |= FRAME_WIDTH_BITS(frame_width);
+ temp_reg |= FRAME_PERIOD_BITS(frame_per);
+ writel(temp_reg, msp->registers + MSP_SRG);
+
+ msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
+
+ /* Enable bit-clock */
+ udelay(100);
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR);
+ udelay(100);
+
+ return 0;
+}
+
+static int configure_multichannel(struct ux500_msp *msp,
+ struct ux500_msp_config *config)
+{
+ struct msp_protdesc *protdesc;
+ struct msp_multichannel_config *mcfg;
+ u32 reg_val_MCR;
+
+ if (config->default_protdesc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ dev_err(msp->dev,
+ "%s: ERROR: Invalid protocol (%d)!\n",
+ __func__, config->protocol);
+ return -EINVAL;
+ }
+ protdesc = (struct msp_protdesc *)
+ &prot_descs[config->protocol];
+ } else {
+ protdesc = (struct msp_protdesc *)&config->protdesc;
+ }
+
+ mcfg = &config->multichannel_config;
+ if (mcfg->tx_multichannel_enable) {
+ if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR | (mcfg->tx_multichannel_enable ?
+ 1 << TMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->tx_channel_0_enable,
+ msp->registers + MSP_TCE0);
+ writel(mcfg->tx_channel_1_enable,
+ msp->registers + MSP_TCE1);
+ writel(mcfg->tx_channel_2_enable,
+ msp->registers + MSP_TCE2);
+ writel(mcfg->tx_channel_3_enable,
+ msp->registers + MSP_TCE3);
+ } else {
+ dev_err(msp->dev,
+ "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
+ __func__, protdesc->tx_phase_mode);
+ return -EINVAL;
+ }
+ }
+ if (mcfg->rx_multichannel_enable) {
+ if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR | (mcfg->rx_multichannel_enable ?
+ 1 << RMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->rx_channel_0_enable,
+ msp->registers + MSP_RCE0);
+ writel(mcfg->rx_channel_1_enable,
+ msp->registers + MSP_RCE1);
+ writel(mcfg->rx_channel_2_enable,
+ msp->registers + MSP_RCE2);
+ writel(mcfg->rx_channel_3_enable,
+ msp->registers + MSP_RCE3);
+ } else {
+ dev_err(msp->dev,
+ "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
+ __func__, protdesc->rx_phase_mode);
+ return -EINVAL;
+ }
+ if (mcfg->rx_comparison_enable_mode) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->rx_comparison_enable_mode << RCMPM_BIT),
+ msp->registers + MSP_MCR);
+
+ writel(mcfg->comparison_mask,
+ msp->registers + MSP_RCM);
+ writel(mcfg->comparison_value,
+ msp->registers + MSP_RCV);
+
+ }
+ }
+
+ return 0;
+}
+
+static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+ int status = 0;
+ u32 reg_val_DMACR, reg_val_GCR;
+
+ /* Check msp state whether in RUN or CONFIGURED Mode */
+ if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) {
+ status = msp->plat_init();
+ if (status) {
+ dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n",
+ __func__, status);
+ return status;
+ }
+ }
+
+ /* Configure msp with protocol dependent settings */
+ configure_protocol(msp, config);
+ setup_bitclk(msp, config);
+ if (config->multichannel_configured == 1) {
+ status = configure_multichannel(msp, config);
+ if (status)
+ dev_warn(msp->dev,
+ "%s: WARN: configure_multichannel failed (%d)!\n",
+ __func__, status);
+ }
+
+ /* Make sure the correct DMA-directions are configured */
+ if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) {
+ 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)) {
+ dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
+ __func__);
+ return -EINVAL;
+ }
+
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ if (config->direction & MSP_DIR_RX)
+ reg_val_DMACR |= RX_DMA_ENABLE;
+ if (config->direction & MSP_DIR_TX)
+ reg_val_DMACR |= TX_DMA_ENABLE;
+ writel(reg_val_DMACR, msp->registers + MSP_DMACR);
+
+ writel(config->iodelay, msp->registers + MSP_IODLY);
+
+ /* Enable frame generation logic */
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
+
+ return status;
+}
+
+static void flush_fifo_rx(struct ux500_msp *msp)
+{
+ u32 reg_val_DR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) {
+ reg_val_DR = readl(msp->registers + MSP_DR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+static void flush_fifo_tx(struct ux500_msp *msp)
+{
+ u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR);
+ writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) {
+ reg_val_TSTDR = readl(msp->registers + MSP_TSTDR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+ writel(0x0, msp->registers + MSP_ITCR);
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+int ux500_msp_i2s_open(struct ux500_msp *msp,
+ struct ux500_msp_config *config)
+{
+ u32 old_reg, new_reg, mask;
+ int res;
+ unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
+
+ if (in_interrupt()) {
+ dev_err(msp->dev,
+ "%s: ERROR: Open called in interrupt context!\n",
+ __func__);
+ return -1;
+ }
+
+ tx_sel = (config->direction & MSP_DIR_TX) > 0;
+ rx_sel = (config->direction & MSP_DIR_RX) > 0;
+ if (!tx_sel && !rx_sel) {
+ dev_err(msp->dev, "%s: Error: No direction selected!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0;
+ rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0;
+ if (tx_busy && tx_sel) {
+ dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__);
+ return -EBUSY;
+ }
+ if (rx_busy && rx_sel) {
+ dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__);
+ return -EBUSY;
+ }
+
+ msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
+
+ /* First do the global config register */
+ mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
+ TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+ RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+ LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+ new_reg = (config->tx_clk_sel | config->rx_clk_sel |
+ config->rx_fsync_pol | config->tx_fsync_pol |
+ config->rx_fsync_sel | config->tx_fsync_sel |
+ config->rx_fifo_config | config->tx_fifo_config |
+ config->srg_clk_sel | config->loopback_enable |
+ config->tx_data_enable);
+
+ old_reg = readl(msp->registers + MSP_GCR);
+ old_reg &= ~mask;
+ new_reg |= old_reg;
+ writel(new_reg, msp->registers + MSP_GCR);
+
+ res = enable_msp(msp, config);
+ if (res < 0) {
+ dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
+ __func__, res);
+ return -EBUSY;
+ }
+ if (config->loopback_enable & 0x80)
+ msp->loopback_enable = 1;
+
+ /* Flush FIFOs */
+ flush_fifo_tx(msp);
+ flush_fifo_rx(msp);
+
+ msp->msp_state = MSP_STATE_CONFIGURED;
+ return 0;
+}
+
+static void disable_msp_rx(struct ux500_msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
+ msp->registers + MSP_IMSC);
+
+ msp->dir_busy &= ~MSP_DIR_RX;
+}
+
+static void disable_msp_tx(struct ux500_msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
+ msp->registers + MSP_IMSC);
+
+ msp->dir_busy &= ~MSP_DIR_TX;
+}
+
+static int disable_msp(struct ux500_msp *msp, unsigned int dir)
+{
+ u32 reg_val_GCR;
+ int status = 0;
+ unsigned int disable_tx, disable_rx;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ disable_tx = dir & MSP_DIR_TX;
+ disable_rx = dir & MSP_DIR_TX;
+ if (disable_tx && disable_rx) {
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | LOOPBACK_MASK,
+ msp->registers + MSP_GCR);
+
+ /* Flush TX-FIFO */
+ flush_fifo_tx(msp);
+
+ /* Disable TX-channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+
+ /* Flush RX-FIFO */
+ flush_fifo_rx(msp);
+
+ /* Disable Loopback and Receive channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(RX_ENABLE | LOOPBACK_MASK))),
+ msp->registers + MSP_GCR);
+
+ disable_msp_tx(msp);
+ disable_msp_rx(msp);
+ } else if (disable_tx)
+ disable_msp_tx(msp);
+ else if (disable_rx)
+ disable_msp_rx(msp);
+
+ return status;
+}
+
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
+{
+ u32 reg_val_GCR, enable_bit;
+
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+ enable_bit = TX_ENABLE;
+ else
+ enable_bit = RX_ENABLE;
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+ disable_msp_tx(msp);
+ else
+ disable_msp_rx(msp);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
+{
+ int status = 0;
+
+ dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
+
+ status = disable_msp(msp, dir);
+ if (msp->dir_busy == 0) {
+ /* disable sample rate and frame generators */
+ msp->msp_state = MSP_STATE_IDLE;
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+ if (msp->plat_exit)
+ status = msp->plat_exit();
+ if (status)
+ dev_warn(msp->dev,
+ "%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
+ __func__, status);
+ writel(0, msp->registers + MSP_GCR);
+ writel(0, msp->registers + MSP_TCF);
+ writel(0, msp->registers + MSP_RCF);
+ writel(0, msp->registers + MSP_DMACR);
+ writel(0, msp->registers + MSP_SRG);
+ writel(0, msp->registers + MSP_MCR);
+ writel(0, msp->registers + MSP_RCM);
+ writel(0, msp->registers + MSP_RCV);
+ writel(0, msp->registers + MSP_TCE0);
+ writel(0, msp->registers + MSP_TCE1);
+ writel(0, msp->registers + MSP_TCE2);
+ writel(0, msp->registers + MSP_TCE3);
+ writel(0, msp->registers + MSP_RCE0);
+ writel(0, msp->registers + MSP_RCE1);
+ writel(0, msp->registers + MSP_RCE2);
+ writel(0, msp->registers + MSP_RCE3);
+ }
+
+ return status;
+
+}
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+ struct ux500_msp **msp_p,
+ struct msp_i2s_platform_data *platform_data)
+{
+ int ret = 0;
+ struct resource *res = NULL;
+ struct i2s_controller *i2s_cont;
+ struct ux500_msp *msp;
+
+ dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
+ pdev->name, platform_data->id);
+
+ *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
+ msp = *msp_p;
+
+ msp->id = platform_data->id;
+ msp->dev = &pdev->dev;
+ msp->plat_init = platform_data->msp_i2s_init;
+ msp->plat_exit = platform_data->msp_i2s_exit;
+ msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+ msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_res;
+ }
+
+ msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ if (msp->registers == NULL) {
+ dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
+ ret = -ENOMEM;
+ goto err_res;
+ }
+
+ 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__);
+ goto err_i2s_cont;
+ }
+ 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;
+
+ return 0;
+
+err_i2s_cont:
+ iounmap(msp->registers);
+
+err_res:
+ devm_kfree(&pdev->dev, msp);
+
+ return ret;
+}
+
+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);
+ devm_kfree(&pdev->dev, msp->i2s_cont);
+
+ iounmap(msp->registers);
+
+ devm_kfree(&pdev->dev, msp);
+}
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
new file mode 100644
index 00000000000..7f71b4a0d4b
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_MSP_I2S_H
+#define UX500_MSP_I2S_H
+
+#include <linux/platform_device.h>
+
+#include <mach/board-mop500-msp.h>
+
+#define MSP_INPUT_FREQ_APB 48000000
+
+/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono),
+ * 32 bits accesses (stereo).
+ ***/
+enum msp_stereo_mode {
+ MSP_MONO,
+ MSP_STEREO
+};
+
+/* Direction (Transmit/Receive mode) */
+enum msp_direction {
+ MSP_TX = 1,
+ MSP_RX = 2
+};
+
+/* Transmit and receive configuration register */
+#define MSP_BIG_ENDIAN 0x00000000
+#define MSP_LITTLE_ENDIAN 0x00001000
+#define MSP_UNEXPECTED_FS_ABORT 0x00000000
+#define MSP_UNEXPECTED_FS_IGNORE 0x00008000
+#define MSP_NON_MODE_BIT_MASK 0x00009000
+
+/* Global configuration register */
+#define RX_ENABLE 0x00000001
+#define RX_FIFO_ENABLE 0x00000002
+#define RX_SYNC_SRG 0x00000010
+#define RX_CLK_POL_RISING 0x00000020
+#define RX_CLK_SEL_SRG 0x00000040
+#define TX_ENABLE 0x00000100
+#define TX_FIFO_ENABLE 0x00000200
+#define TX_SYNC_SRG_PROG 0x00001800
+#define TX_SYNC_SRG_AUTO 0x00001000
+#define TX_CLK_POL_RISING 0x00002000
+#define TX_CLK_SEL_SRG 0x00004000
+#define TX_EXTRA_DELAY_ENABLE 0x00008000
+#define SRG_ENABLE 0x00010000
+#define FRAME_GEN_ENABLE 0x00100000
+#define SRG_CLK_SEL_APB 0x00000000
+#define RX_FIFO_SYNC_HI 0x00000000
+#define TX_FIFO_SYNC_HI 0x00000000
+#define SPI_CLK_MODE_NORMAL 0x00000000
+
+#define MSP_FRAME_SIZE_AUTO -1
+
+#define MSP_DR 0x00
+#define MSP_GCR 0x04
+#define MSP_TCF 0x08
+#define MSP_RCF 0x0c
+#define MSP_SRG 0x10
+#define MSP_FLR 0x14
+#define MSP_DMACR 0x18
+
+#define MSP_IMSC 0x20
+#define MSP_RIS 0x24
+#define MSP_MIS 0x28
+#define MSP_ICR 0x2c
+#define MSP_MCR 0x30
+#define MSP_RCV 0x34
+#define MSP_RCM 0x38
+
+#define MSP_TCE0 0x40
+#define MSP_TCE1 0x44
+#define MSP_TCE2 0x48
+#define MSP_TCE3 0x4c
+
+#define MSP_RCE0 0x60
+#define MSP_RCE1 0x64
+#define MSP_RCE2 0x68
+#define MSP_RCE3 0x6c
+#define MSP_IODLY 0x70
+
+#define MSP_ITCR 0x80
+#define MSP_ITIP 0x84
+#define MSP_ITOP 0x88
+#define MSP_TSTDR 0x8c
+
+#define MSP_PID0 0xfe0
+#define MSP_PID1 0xfe4
+#define MSP_PID2 0xfe8
+#define MSP_PID3 0xfec
+
+#define MSP_CID0 0xff0
+#define MSP_CID1 0xff4
+#define MSP_CID2 0xff8
+#define MSP_CID3 0xffc
+
+/* Protocol dependant parameters list */
+#define RX_ENABLE_MASK BIT(0)
+#define RX_FIFO_ENABLE_MASK BIT(1)
+#define RX_FSYNC_MASK BIT(2)
+#define DIRECT_COMPANDING_MASK BIT(3)
+#define RX_SYNC_SEL_MASK BIT(4)
+#define RX_CLK_POL_MASK BIT(5)
+#define RX_CLK_SEL_MASK BIT(6)
+#define LOOPBACK_MASK BIT(7)
+#define TX_ENABLE_MASK BIT(8)
+#define TX_FIFO_ENABLE_MASK BIT(9)
+#define TX_FSYNC_MASK BIT(10)
+#define TX_MSP_TDR_TSR BIT(11)
+#define TX_SYNC_SEL_MASK (BIT(12) | BIT(11))
+#define TX_CLK_POL_MASK BIT(13)
+#define TX_CLK_SEL_MASK BIT(14)
+#define TX_EXTRA_DELAY_MASK BIT(15)
+#define SRG_ENABLE_MASK BIT(16)
+#define SRG_CLK_POL_MASK BIT(17)
+#define SRG_CLK_SEL_MASK (BIT(19) | BIT(18))
+#define FRAME_GEN_EN_MASK BIT(20)
+#define SPI_CLK_MODE_MASK (BIT(22) | BIT(21))
+#define SPI_BURST_MODE_MASK BIT(23)
+
+#define RXEN_SHIFT 0
+#define RFFEN_SHIFT 1
+#define RFSPOL_SHIFT 2
+#define DCM_SHIFT 3
+#define RFSSEL_SHIFT 4
+#define RCKPOL_SHIFT 5
+#define RCKSEL_SHIFT 6
+#define LBM_SHIFT 7
+#define TXEN_SHIFT 8
+#define TFFEN_SHIFT 9
+#define TFSPOL_SHIFT 10
+#define TFSSEL_SHIFT 11
+#define TCKPOL_SHIFT 13
+#define TCKSEL_SHIFT 14
+#define TXDDL_SHIFT 15
+#define SGEN_SHIFT 16
+#define SCKPOL_SHIFT 17
+#define SCKSEL_SHIFT 18
+#define FGEN_SHIFT 20
+#define SPICKM_SHIFT 21
+#define TBSWAP_SHIFT 28
+
+#define RCKPOL_MASK BIT(0)
+#define TCKPOL_MASK BIT(0)
+#define SPICKM_MASK (BIT(1) | BIT(0))
+#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
+#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
+
+#define P1ELEN_SHIFT 0
+#define P1FLEN_SHIFT 3
+#define DTYP_SHIFT 10
+#define ENDN_SHIFT 12
+#define DDLY_SHIFT 13
+#define FSIG_SHIFT 15
+#define P2ELEN_SHIFT 16
+#define P2FLEN_SHIFT 19
+#define P2SM_SHIFT 26
+#define P2EN_SHIFT 27
+#define FSYNC_SHIFT 15
+
+#define P1ELEN_MASK 0x00000007
+#define P2ELEN_MASK 0x00070000
+#define P1FLEN_MASK 0x00000378
+#define P2FLEN_MASK 0x03780000
+#define DDLY_MASK 0x00003000
+#define DTYP_MASK 0x00000600
+#define P2SM_MASK 0x04000000
+#define P2EN_MASK 0x08000000
+#define ENDN_MASK 0x00001000
+#define TFSPOL_MASK 0x00000400
+#define TBSWAP_MASK 0x30000000
+#define COMPANDING_MODE_MASK 0x00000c00
+#define FSYNC_MASK 0x00008000
+
+#define MSP_P1_ELEM_LEN_BITS(n) (n & P1ELEN_MASK)
+#define MSP_P2_ELEM_LEN_BITS(n) (((n) << P2ELEN_SHIFT) & P2ELEN_MASK)
+#define MSP_P1_FRAME_LEN_BITS(n) (((n) << P1FLEN_SHIFT) & P1FLEN_MASK)
+#define MSP_P2_FRAME_LEN_BITS(n) (((n) << P2FLEN_SHIFT) & P2FLEN_MASK)
+#define MSP_DATA_DELAY_BITS(n) (((n) << DDLY_SHIFT) & DDLY_MASK)
+#define MSP_DATA_TYPE_BITS(n) (((n) << DTYP_SHIFT) & DTYP_MASK)
+#define MSP_P2_START_MODE_BIT(n) ((n << P2SM_SHIFT) & P2SM_MASK)
+#define MSP_P2_ENABLE_BIT(n) ((n << P2EN_SHIFT) & P2EN_MASK)
+#define MSP_SET_ENDIANNES_BIT(n) ((n << ENDN_SHIFT) & ENDN_MASK)
+#define MSP_FSYNC_POL(n) ((n << TFSPOL_SHIFT) & TFSPOL_MASK)
+#define MSP_DATA_WORD_SWAP(n) ((n << TBSWAP_SHIFT) & TBSWAP_MASK)
+#define MSP_SET_COMPANDING_MODE(n) ((n << DTYP_SHIFT) & \
+ COMPANDING_MODE_MASK)
+#define MSP_SET_FSYNC_IGNORE(n) ((n << FSYNC_SHIFT) & FSYNC_MASK)
+
+/* Flag register */
+#define RX_BUSY BIT(0)
+#define RX_FIFO_EMPTY BIT(1)
+#define RX_FIFO_FULL BIT(2)
+#define TX_BUSY BIT(3)
+#define TX_FIFO_EMPTY BIT(4)
+#define TX_FIFO_FULL BIT(5)
+
+#define RBUSY_SHIFT 0
+#define RFE_SHIFT 1
+#define RFU_SHIFT 2
+#define TBUSY_SHIFT 3
+#define TFE_SHIFT 4
+#define TFU_SHIFT 5
+
+/* Multichannel control register */
+#define RMCEN_SHIFT 0
+#define RMCSF_SHIFT 1
+#define RCMPM_SHIFT 3
+#define TMCEN_SHIFT 5
+#define TNCSF_SHIFT 6
+
+/* Sample rate generator register */
+#define SCKDIV_SHIFT 0
+#define FRWID_SHIFT 10
+#define FRPER_SHIFT 16
+
+#define SCK_DIV_MASK 0x0000003FF
+#define FRAME_WIDTH_BITS(n) (((n) << FRWID_SHIFT) & 0x0000FC00)
+#define FRAME_PERIOD_BITS(n) (((n) << FRPER_SHIFT) & 0x1FFF0000)
+
+/* DMA controller register */
+#define RX_DMA_ENABLE BIT(0)
+#define TX_DMA_ENABLE BIT(1)
+
+#define RDMAE_SHIFT 0
+#define TDMAE_SHIFT 1
+
+/* Interrupt Register */
+#define RX_SERVICE_INT BIT(0)
+#define RX_OVERRUN_ERROR_INT BIT(1)
+#define RX_FSYNC_ERR_INT BIT(2)
+#define RX_FSYNC_INT BIT(3)
+#define TX_SERVICE_INT BIT(4)
+#define TX_UNDERRUN_ERR_INT BIT(5)
+#define TX_FSYNC_ERR_INT BIT(6)
+#define TX_FSYNC_INT BIT(7)
+#define ALL_INT 0x000000ff
+
+/* MSP test control register */
+#define MSP_ITCR_ITEN BIT(0)
+#define MSP_ITCR_TESTFIFO BIT(1)
+
+#define RMCEN_BIT 0
+#define RMCSF_BIT 1
+#define RCMPM_BIT 3
+#define TMCEN_BIT 5
+#define TNCSF_BIT 6
+
+/* Single or dual phase mode */
+enum msp_phase_mode {
+ MSP_SINGLE_PHASE,
+ MSP_DUAL_PHASE
+};
+
+/* Frame length */
+enum msp_frame_length {
+ MSP_FRAME_LEN_1 = 0,
+ MSP_FRAME_LEN_2 = 1,
+ MSP_FRAME_LEN_4 = 3,
+ MSP_FRAME_LEN_8 = 7,
+ MSP_FRAME_LEN_12 = 11,
+ MSP_FRAME_LEN_16 = 15,
+ MSP_FRAME_LEN_20 = 19,
+ MSP_FRAME_LEN_32 = 31,
+ MSP_FRAME_LEN_48 = 47,
+ MSP_FRAME_LEN_64 = 63
+};
+
+/* Element length */
+enum msp_elem_length {
+ MSP_ELEM_LEN_8 = 0,
+ MSP_ELEM_LEN_10 = 1,
+ MSP_ELEM_LEN_12 = 2,
+ MSP_ELEM_LEN_14 = 3,
+ MSP_ELEM_LEN_16 = 4,
+ MSP_ELEM_LEN_20 = 5,
+ MSP_ELEM_LEN_24 = 6,
+ MSP_ELEM_LEN_32 = 7
+};
+
+enum msp_data_xfer_width {
+ MSP_DATA_TRANSFER_WIDTH_BYTE,
+ MSP_DATA_TRANSFER_WIDTH_HALFWORD,
+ MSP_DATA_TRANSFER_WIDTH_WORD
+};
+
+enum msp_frame_sync {
+ MSP_FSYNC_UNIGNORE = 0,
+ MSP_FSYNC_IGNORE = 1,
+};
+
+enum msp_phase2_start_mode {
+ MSP_PHASE2_START_MODE_IMEDIATE,
+ MSP_PHASE2_START_MODE_FSYNC
+};
+
+enum msp_btf {
+ MSP_BTF_MS_BIT_FIRST = 0,
+ MSP_BTF_LS_BIT_FIRST = 1
+};
+
+enum msp_fsync_pol {
+ MSP_FSYNC_POL_ACT_HI = 0,
+ MSP_FSYNC_POL_ACT_LO = 1
+};
+
+/* Data delay (in bit clock cycles) */
+enum msp_delay {
+ MSP_DELAY_0 = 0,
+ MSP_DELAY_1 = 1,
+ MSP_DELAY_2 = 2,
+ MSP_DELAY_3 = 3
+};
+
+/* Configurations of clocks (transmit, receive or sample rate generator) */
+enum msp_edge {
+ MSP_FALLING_EDGE = 0,
+ MSP_RISING_EDGE = 1,
+};
+
+enum msp_hws {
+ MSP_SWAP_NONE = 0,
+ MSP_SWAP_BYTE_PER_WORD = 1,
+ MSP_SWAP_BYTE_PER_HALF_WORD = 2,
+ MSP_SWAP_HALF_WORD_PER_WORD = 3
+};
+
+enum msp_compress_mode {
+ MSP_COMPRESS_MODE_LINEAR = 0,
+ MSP_COMPRESS_MODE_MU_LAW = 2,
+ 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,
+ MSP_EXPAND_MODE_MU_LAW = 2,
+ MSP_EXPAND_MODE_A_LAW = 3
+};
+
+#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
+#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
+#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
+
+enum msp_protocol {
+ MSP_I2S_PROTOCOL,
+ MSP_PCM_PROTOCOL,
+ MSP_PCM_COMPAND_PROTOCOL,
+ MSP_INVALID_PROTOCOL
+};
+
+/*
+ * No of registers to backup during
+ * suspend resume
+ */
+#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,
+};
+
+enum msp_data_size {
+ MSP_DATA_BITS_DEFAULT = -1,
+ MSP_DATA_BITS_8 = 0x00,
+ MSP_DATA_BITS_10,
+ MSP_DATA_BITS_12,
+ MSP_DATA_BITS_14,
+ MSP_DATA_BITS_16,
+ MSP_DATA_BITS_20,
+ MSP_DATA_BITS_24,
+ MSP_DATA_BITS_32,
+};
+
+enum msp_state {
+ MSP_STATE_IDLE = 0,
+ MSP_STATE_CONFIGURED = 1,
+ MSP_STATE_RUNNING = 2,
+};
+
+enum msp_rx_comparison_enable_mode {
+ MSP_COMPARISON_DISABLED = 0,
+ MSP_COMPARISON_NONEQUAL_ENABLED = 2,
+ MSP_COMPARISON_EQUAL_ENABLED = 3
+};
+
+struct msp_multichannel_config {
+ bool rx_multichannel_enable;
+ bool tx_multichannel_enable;
+ enum msp_rx_comparison_enable_mode rx_comparison_enable_mode;
+ u8 padding;
+ u32 comparison_value;
+ u32 comparison_mask;
+ u32 rx_channel_0_enable;
+ u32 rx_channel_1_enable;
+ u32 rx_channel_2_enable;
+ u32 rx_channel_3_enable;
+ u32 tx_channel_0_enable;
+ u32 tx_channel_1_enable;
+ u32 tx_channel_2_enable;
+ u32 tx_channel_3_enable;
+};
+
+struct msp_protdesc {
+ u32 rx_phase_mode;
+ u32 tx_phase_mode;
+ u32 rx_phase2_start_mode;
+ u32 tx_phase2_start_mode;
+ u32 rx_byte_order;
+ u32 tx_byte_order;
+ u32 rx_frame_len_1;
+ u32 rx_frame_len_2;
+ u32 tx_frame_len_1;
+ u32 tx_frame_len_2;
+ u32 rx_elem_len_1;
+ u32 rx_elem_len_2;
+ u32 tx_elem_len_1;
+ u32 tx_elem_len_2;
+ u32 rx_data_delay;
+ u32 tx_data_delay;
+ u32 rx_clk_pol;
+ u32 tx_clk_pol;
+ u32 rx_fsync_pol;
+ u32 tx_fsync_pol;
+ u32 rx_half_word_swap;
+ u32 tx_half_word_swap;
+ u32 compression_mode;
+ u32 expansion_mode;
+ u32 frame_sync_ignore;
+ u32 frame_period;
+ u32 frame_width;
+ 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;
+ unsigned int tx_clk_sel;
+ unsigned int srg_clk_sel;
+ unsigned int rx_fsync_pol;
+ unsigned int tx_fsync_pol;
+ unsigned int rx_fsync_sel;
+ 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;
+ struct msp_protdesc protdesc;
+ int multichannel_configured;
+ struct msp_multichannel_config multichannel_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 {
+ enum enum_i2s_controller 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;
+ enum msp_state msp_state;
+ int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
+ int (*plat_init) (void);
+ int (*plat_exit) (void);
+ 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;
+};
+
+struct ux500_msp_dma_params {
+ unsigned int data_size;
+ struct stedma40_chan_cfg *dma_cfg;
+};
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+ struct ux500_msp **msp_p,
+ struct msp_i2s_platform_data *platform_data);
+void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
+ struct ux500_msp *msp);
+int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config);
+int ux500_msp_i2s_close(struct ux500_msp *msp,
+ unsigned int dir);
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd,
+ int direction);
+
+#endif
diff --git a/sound/sound_core.c b/sound/sound_core.c
index c6e81fb928e..fb9255cca21 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -361,7 +361,7 @@ int register_sound_special_device(const struct file_operations *fops, int unit,
struct device *dev)
{
const int chain = unit % SOUND_STEP;
- int max_unit = 128 + chain;
+ int max_unit = 256;
const char *name;
char _name[16];
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4a7be7b9833..d5b5c3388e2 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
subs = &as->substream[idx];
if (!subs->num_formats)
continue;
- snd_usb_release_substream_urbs(subs, 1);
subs->interface = -1;
+ subs->data_endpoint = NULL;
+ subs->sync_endpoint = NULL;
}
}
@@ -276,6 +277,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
static int snd_usb_audio_free(struct snd_usb_audio *chip)
{
+ mutex_destroy(&chip->mutex);
kfree(chip);
return 0;
}
@@ -336,6 +338,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
return -ENOMEM;
}
+ mutex_init(&chip->mutex);
mutex_init(&chip->shutdown_mutex);
chip->index = idx;
chip->dev = dev;
@@ -348,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->midi_list);
INIT_LIST_HEAD(&chip->mixer_list);
@@ -565,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
list_for_each(p, &chip->pcm_list) {
snd_usb_stream_disconnect(p);
}
+ /* release the endpoint resources */
+ list_for_each(p, &chip->ep_list) {
+ snd_usb_endpoint_free(p);
+ }
/* release the midi resources */
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index da5fa1ac4ed..0d37238b845 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -30,20 +30,71 @@ struct audioformat {
};
struct snd_usb_substream;
+struct snd_usb_endpoint;
struct snd_urb_ctx {
struct urb *urb;
unsigned int buffer_size; /* size of data buffer, if data URB */
struct snd_usb_substream *subs;
+ struct snd_usb_endpoint *ep;
int index; /* index for urb array */
int packets; /* number of packets per urb */
+ int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */
+ struct list_head ready_list;
};
-struct snd_urb_ops {
- int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+struct snd_usb_endpoint {
+ struct snd_usb_audio *chip;
+
+ int use_count;
+ int ep_num; /* the referenced endpoint number */
+ int type; /* SND_USB_ENDPOINT_TYPE_* */
+ unsigned long flags;
+
+ void (*prepare_data_urb) (struct snd_usb_substream *subs,
+ struct urb *urb);
+ void (*retire_data_urb) (struct snd_usb_substream *subs,
+ struct urb *urb);
+
+ struct snd_usb_substream *data_subs;
+ struct snd_usb_endpoint *sync_master;
+ struct snd_usb_endpoint *sync_slave;
+
+ struct snd_urb_ctx urb[MAX_URBS];
+
+ struct snd_usb_packet_info {
+ uint32_t packet_size[MAX_PACKS_HS];
+ int packets;
+ } next_packet[MAX_URBS];
+ int next_packet_read_pos, next_packet_write_pos;
+ struct list_head ready_playback_urbs;
+
+ unsigned int nurbs; /* # urbs */
+ unsigned long active_mask; /* bitmask of active urbs */
+ unsigned long unlink_mask; /* bitmask of unlinked urbs */
+ char *syncbuf; /* sync buffer for all sync URBs */
+ dma_addr_t sync_dma; /* DMA address of syncbuf */
+
+ unsigned int pipe; /* the data i/o pipe */
+ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
+ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
+ int freqshift; /* how much to shift the feedback value to get Q16.16 */
+ unsigned int freqmax; /* maximum sampling rate, used for buffer management */
+ unsigned int phase; /* phase accumulator */
+ unsigned int maxpacksize; /* max packet size in bytes */
+ unsigned int maxframesize; /* max packet size in frames */
+ unsigned int curpacksize; /* current packet size in bytes (for capture) */
+ unsigned int curframesize; /* current packet size in frames (for capture) */
+ unsigned int syncmaxsize; /* sync endpoint packet size */
+ unsigned int fill_max:1; /* fill max packet size always */
+ unsigned int datainterval; /* log_2 of data packet interval */
+ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
+ unsigned char silence_value;
+ unsigned int stride;
+ int iface, alt_idx;
+
+ spinlock_t lock;
+ struct list_head list;
};
struct snd_usb_substream {
@@ -57,21 +108,6 @@ struct snd_usb_substream {
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
unsigned int altset_idx; /* USB data format: index of alternate setting */
- unsigned int datapipe; /* the data i/o pipe */
- unsigned int syncpipe; /* 1 - async out or adaptive in */
- unsigned int datainterval; /* log_2 of data packet interval */
- unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
- unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
- unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
- int freqshift; /* how much to shift the feedback value to get Q16.16 */
- unsigned int freqmax; /* maximum sampling rate, used for buffer management */
- unsigned int phase; /* phase accumulator */
- unsigned int maxpacksize; /* max packet size in bytes */
- unsigned int maxframesize; /* max packet size in frames */
- unsigned int curpacksize; /* current packet size in bytes (for capture) */
- unsigned int curframesize; /* current packet size in frames (for capture) */
- unsigned int syncmaxsize; /* sync endpoint packet size */
- unsigned int fill_max: 1; /* fill max packet size always */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
unsigned int fmt_type; /* USB audio format type (1-3) */
@@ -82,11 +118,10 @@ struct snd_usb_substream {
unsigned long active_mask; /* bitmask of active urbs */
unsigned long unlink_mask; /* bitmask of unlinked urbs */
- unsigned int nurbs; /* # urbs */
- struct snd_urb_ctx dataurb[MAX_URBS]; /* data urb table */
- struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */
- char *syncbuf; /* sync buffer for all sync URBs */
- dma_addr_t sync_dma; /* DMA address of syncbuf */
+ /* data and sync endpoints for this stream */
+ struct snd_usb_endpoint *data_endpoint;
+ struct snd_usb_endpoint *sync_endpoint;
+ unsigned long flags;
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
@@ -94,7 +129,6 @@ struct snd_usb_substream {
struct snd_pcm_hw_constraint_list rate_list; /* limited rates */
spinlock_t lock;
- struct snd_urb_ops ops; /* callbacks (must be filled at init) */
int last_frame_number; /* stored frame number */
int last_delay; /* stored delay */
};
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 08dcce53720..e6906901deb 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -20,9 +20,11 @@
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include "usbaudio.h"
#include "helper.h"
@@ -30,6 +32,36 @@
#include "endpoint.h"
#include "pcm.h"
+#define EP_FLAG_ACTIVATED 0
+#define EP_FLAG_RUNNING 1
+
+/*
+ * snd_usb_endpoint is a model that abstracts everything related to an
+ * USB endpoint and its streaming.
+ *
+ * There are functions to activate and deactivate the streaming URBs and
+ * optional callbacks to let the pcm logic handle the actual content of the
+ * packets for playback and record. Thus, the bus streaming and the audio
+ * handlers are fully decoupled.
+ *
+ * There are two different types of endpoints in audio applications.
+ *
+ * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both
+ * inbound and outbound traffic.
+ *
+ * SND_USB_ENDPOINT_TYPE_SYNC endpoints are for inbound traffic only and
+ * expect the payload to carry Q10.14 / Q16.16 formatted sync information
+ * (3 or 4 bytes).
+ *
+ * Each endpoint has to be configured prior to being used by calling
+ * snd_usb_endpoint_set_params().
+ *
+ * The model incorporates a reference counting, so that multiple users
+ * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and
+ * only the first user will effectively start the URBs, and only the last
+ * one to stop it will tear the URBs down again.
+ */
+
/*
* convert a sampling rate into our full speed format (fs/1000 in Q16.16)
* this will overflow at approx 524 kHz
@@ -49,71 +81,415 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
}
/*
- * unlink active urbs.
+ * release a urb data
*/
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static void release_urb_ctx(struct snd_urb_ctx *u)
{
- struct snd_usb_audio *chip = subs->stream->chip;
- unsigned int i;
- int async;
+ if (u->buffer_size)
+ usb_free_coherent(u->ep->chip->dev, u->buffer_size,
+ u->urb->transfer_buffer,
+ u->urb->transfer_dma);
+ usb_free_urb(u->urb);
+ u->urb = NULL;
+}
+
+static const char *usb_error_string(int err)
+{
+ switch (err) {
+ case -ENODEV:
+ return "no device";
+ case -ENOENT:
+ return "endpoint not enabled";
+ case -EPIPE:
+ return "endpoint stalled";
+ case -ENOSPC:
+ return "not enough bandwidth";
+ case -ESHUTDOWN:
+ return "device disabled";
+ case -EHOSTUNREACH:
+ return "device suspended";
+ case -EINVAL:
+ case -EAGAIN:
+ case -EFBIG:
+ case -EMSGSIZE:
+ return "internal error";
+ default:
+ return "unknown error";
+ }
+}
+
+/**
+ * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type
+ *
+ * @ep: The snd_usb_endpoint
+ *
+ * Determine whether an endpoint is driven by an implicit feedback
+ * data endpoint source.
+ */
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+ return ep->sync_master &&
+ ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+ ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+ usb_pipeout(ep->pipe);
+}
- subs->running = 0;
+/*
+ * For streaming based on information derived from sync endpoints,
+ * prepare_outbound_urb_sizes() will call next_packet_size() to
+ * determine the number of samples to be sent in the next packet.
+ *
+ * For implicit feedback, next_packet_size() is unused.
+ */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+ unsigned long flags;
+ int ret;
- if (!force && subs->stream->chip->shutdown) /* to be sure... */
- return -EBADFD;
+ if (ep->fill_max)
+ return ep->maxframesize;
- async = !can_sleep && chip->async_unlink;
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->phase = (ep->phase & 0xffff)
+ + (ep->freqm << ep->datainterval);
+ ret = min(ep->phase >> 16, ep->maxframesize);
+ spin_unlock_irqrestore(&ep->lock, flags);
- if (!async && in_interrupt())
- return 0;
+ return ret;
+}
- for (i = 0; i < subs->nurbs; i++) {
- if (test_bit(i, &subs->active_mask)) {
- if (!test_and_set_bit(i, &subs->unlink_mask)) {
- struct urb *u = subs->dataurb[i].urb;
- if (async)
- usb_unlink_urb(u);
- else
- usb_kill_urb(u);
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
+{
+ if (ep->retire_data_urb)
+ ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
+{
+ struct urb *urb = urb_ctx->urb;
+
+ if (ep->sync_slave)
+ snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+ if (ep->retire_data_urb)
+ ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->packets; ++i)
+ ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ int i;
+ struct urb *urb = ctx->urb;
+ unsigned char *cp = urb->transfer_buffer;
+
+ urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ if (ep->prepare_data_urb) {
+ ep->prepare_data_urb(ep->data_subs, urb);
+ } else {
+ /* no data provider, so send silence */
+ unsigned int offs = 0;
+ for (i = 0; i < ctx->packets; ++i) {
+ int counts = ctx->packet_size[i];
+ urb->iso_frame_desc[i].offset = offs * ep->stride;
+ urb->iso_frame_desc[i].length = counts * ep->stride;
+ offs += counts;
}
+
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * ep->stride;
+ memset(urb->transfer_buffer, ep->silence_value,
+ offs * ep->stride);
}
+ break;
+
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+ /*
+ * fill the length and offset of each urb descriptor.
+ * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ */
+ urb->iso_frame_desc[0].length = 4;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = ep->freqn;
+ cp[1] = ep->freqn >> 8;
+ cp[2] = ep->freqn >> 16;
+ cp[3] = ep->freqn >> 24;
+ } else {
+ /*
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+ urb->iso_frame_desc[0].length = 3;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = ep->freqn >> 2;
+ cp[1] = ep->freqn >> 10;
+ cp[2] = ep->freqn >> 18;
+ }
+
+ break;
}
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (test_bit(i+16, &subs->active_mask)) {
- if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
- struct urb *u = subs->syncurb[i].urb;
- if (async)
- usb_unlink_urb(u);
- else
- usb_kill_urb(u);
- }
- }
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
+{
+ int i, offs;
+ struct urb *urb = urb_ctx->urb;
+
+ urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ offs = 0;
+ for (i = 0; i < urb_ctx->packets; i++) {
+ urb->iso_frame_desc[i].offset = offs;
+ urb->iso_frame_desc[i].length = ep->curpacksize;
+ offs += ep->curpacksize;
}
+
+ urb->transfer_buffer_length = offs;
+ urb->number_of_packets = urb_ctx->packets;
+ break;
+
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+ urb->iso_frame_desc[0].offset = 0;
+ break;
}
- return 0;
}
+/*
+ * Send output urbs that have been prepared previously. URBs are dequeued
+ * from ep->ready_playback_urbs and in case there there aren't any available
+ * or there are no packets that have been prepared, this function does
+ * nothing.
+ *
+ * The reason why the functionality of sending and preparing URBs is separated
+ * is that host controllers don't guarantee the order in which they return
+ * inbound and outbound packets to their submitters.
+ *
+ * This function is only used for implicit feedback endpoints. For endpoints
+ * driven by dedicated sync endpoints, URBs are immediately re-submitted
+ * from their completion handler.
+ */
+static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+{
+ while (test_bit(EP_FLAG_RUNNING, &ep->flags)) {
+
+ unsigned long flags;
+ struct snd_usb_packet_info *uninitialized_var(packet);
+ struct snd_urb_ctx *ctx = NULL;
+ struct urb *urb;
+ int err, i;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ if (ep->next_packet_read_pos != ep->next_packet_write_pos) {
+ packet = ep->next_packet + ep->next_packet_read_pos;
+ ep->next_packet_read_pos++;
+ ep->next_packet_read_pos %= MAX_URBS;
+
+ /* take URB out of FIFO */
+ if (!list_empty(&ep->ready_playback_urbs))
+ ctx = list_first_entry(&ep->ready_playback_urbs,
+ struct snd_urb_ctx, ready_list);
+ }
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (ctx == NULL)
+ return;
+
+ list_del_init(&ctx->ready_list);
+ urb = ctx->urb;
+
+ /* copy over the length information */
+ for (i = 0; i < packet->packets; i++)
+ ctx->packet_size[i] = packet->packet_size[i];
+
+ /* call the data handler to fill in playback data */
+ prepare_outbound_urb(ep, ctx);
+
+ err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+ if (err < 0)
+ snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+ ctx->index, err, ctx->urb);
+ else
+ set_bit(ctx->index, &ep->active_mask);
+ }
+}
/*
- * release a urb data
+ * complete callback for urbs
*/
-static void release_urb_ctx(struct snd_urb_ctx *u)
+static void snd_complete_urb(struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+ struct snd_usb_endpoint *ep = ctx->ep;
+ int err;
+
+ if (unlikely(urb->status == -ENOENT || /* unlinked */
+ urb->status == -ENODEV || /* device removed */
+ urb->status == -ECONNRESET || /* unlinked */
+ urb->status == -ESHUTDOWN || /* device disabled */
+ ep->chip->shutdown)) /* device disconnected */
+ goto exit_clear;
+
+ if (usb_pipeout(ep->pipe)) {
+ retire_outbound_urb(ep, ctx);
+ /* can be stopped during retire callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
+ if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ spin_unlock_irqrestore(&ep->lock, flags);
+ queue_pending_output_urbs(ep);
+
+ goto exit_clear;
+ }
+
+ prepare_outbound_urb_sizes(ep, ctx);
+ prepare_outbound_urb(ep, ctx);
+ } else {
+ retire_inbound_urb(ep, ctx);
+ /* can be stopped during retire callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
+ prepare_inbound_urb(ep, ctx);
+ }
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err == 0)
+ return;
+
+ snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+ //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+ clear_bit(ctx->index, &ep->active_mask);
+}
+
+/**
+ * snd_usb_add_endpoint: Add an endpoint to an USB audio chip
+ *
+ * @chip: The chip
+ * @alts: The USB host interface
+ * @ep_num: The number of the endpoint to use
+ * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC
+ *
+ * If the requested endpoint has not been added to the given chip before,
+ * a new instance is created. Otherwise, a pointer to the previoulsy
+ * created instance is returned. In case of any error, NULL is returned.
+ *
+ * New endpoints will be added to chip->ep_list and must be freed by
+ * calling snd_usb_endpoint_free().
+ */
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int ep_num, int direction, int type)
{
- if (u->urb) {
- if (u->buffer_size)
- usb_free_coherent(u->subs->dev, u->buffer_size,
- u->urb->transfer_buffer,
- u->urb->transfer_dma);
- usb_free_urb(u->urb);
- u->urb = NULL;
+ struct list_head *p;
+ struct snd_usb_endpoint *ep;
+ int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+ mutex_lock(&chip->mutex);
+
+ list_for_each(p, &chip->ep_list) {
+ ep = list_entry(p, struct snd_usb_endpoint, list);
+ if (ep->ep_num == ep_num &&
+ ep->iface == alts->desc.bInterfaceNumber &&
+ ep->alt_idx == alts->desc.bAlternateSetting) {
+ snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+ ep_num, ep->iface, ep->alt_idx, ep);
+ goto __exit_unlock;
+ }
+ }
+
+ snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+ is_playback ? "playback" : "capture",
+ type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+ ep_num);
+
+ /* select the alt setting once so the endpoints become valid */
+ ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+ alts->desc.bAlternateSetting);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+ __func__, ret);
+ ep = NULL;
+ goto __exit_unlock;
}
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ goto __exit_unlock;
+
+ ep->chip = chip;
+ spin_lock_init(&ep->lock);
+ ep->type = type;
+ ep->ep_num = ep_num;
+ ep->iface = alts->desc.bInterfaceNumber;
+ ep->alt_idx = alts->desc.bAlternateSetting;
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+ if (is_playback)
+ ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+ else
+ ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+ if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+ if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ get_endpoint(alts, 1)->bRefresh >= 1 &&
+ get_endpoint(alts, 1)->bRefresh <= 9)
+ ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+ else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+ ep->syncinterval = 1;
+ else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+ get_endpoint(alts, 1)->bInterval <= 16)
+ ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+ else
+ ep->syncinterval = 3;
+
+ ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+ }
+
+ list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+ mutex_unlock(&chip->mutex);
+
+ return ep;
}
/*
* wait until all urbs are processed.
*/
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
{
unsigned long end_time = jiffies + msecs_to_jiffies(1000);
unsigned int i;
@@ -121,153 +497,148 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
do {
alive = 0;
- for (i = 0; i < subs->nurbs; i++) {
- if (test_bit(i, &subs->active_mask))
+ for (i = 0; i < ep->nurbs; i++)
+ if (test_bit(i, &ep->active_mask))
alive++;
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (test_bit(i + 16, &subs->active_mask))
- alive++;
- }
- }
- if (! alive)
+
+ if (!alive)
break;
+
schedule_timeout_uninterruptible(1);
} while (time_before(jiffies, end_time));
+
if (alive)
- snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+ snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+ alive, ep->ep_num);
+
return 0;
}
/*
- * release a substream
+ * unlink active urbs.
*/
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
{
- int i;
+ unsigned int i;
+ int async;
- /* stop urbs (to be sure) */
- deactivate_urbs(subs, force, 1);
- wait_clear_urbs(subs);
-
- for (i = 0; i < MAX_URBS; i++)
- release_urb_ctx(&subs->dataurb[i]);
- for (i = 0; i < SYNC_URBS; i++)
- release_urb_ctx(&subs->syncurb[i]);
- usb_free_coherent(subs->dev, SYNC_URBS * 4,
- subs->syncbuf, subs->sync_dma);
- subs->syncbuf = NULL;
- subs->nurbs = 0;
-}
+ if (!force && ep->chip->shutdown) /* to be sure... */
+ return -EBADFD;
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
- struct snd_urb_ctx *ctx = urb->context;
- struct snd_usb_substream *subs = ctx->subs;
- struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
- int err = 0;
-
- if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
- !subs->running || /* can be stopped during retire callback */
- (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
- (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- clear_bit(ctx->index, &subs->active_mask);
- if (err < 0) {
- snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ async = !can_sleep && ep->chip->async_unlink;
+
+ clear_bit(EP_FLAG_RUNNING, &ep->flags);
+
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep->next_packet_read_pos = 0;
+ ep->next_packet_write_pos = 0;
+
+ if (!async && in_interrupt())
+ return 0;
+
+ for (i = 0; i < ep->nurbs; i++) {
+ if (test_bit(i, &ep->active_mask)) {
+ if (!test_and_set_bit(i, &ep->unlink_mask)) {
+ struct urb *u = ep->urb[i].urb;
+ if (async)
+ usb_unlink_urb(u);
+ else
+ usb_kill_urb(u);
+ }
}
}
-}
+ return 0;
+}
/*
- * complete callback from sync urb
+ * release an endpoint's urbs
*/
-static void snd_complete_sync_urb(struct urb *urb)
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
{
- struct snd_urb_ctx *ctx = urb->context;
- struct snd_usb_substream *subs = ctx->subs;
- struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
- int err = 0;
-
- if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
- !subs->running || /* can be stopped during retire callback */
- (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
- (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- clear_bit(ctx->index + 16, &subs->active_mask);
- if (err < 0) {
- snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
- }
-}
+ int i;
+ /* route incoming urbs to nirvana */
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
+ /* stop urbs */
+ deactivate_urbs(ep, force, 1);
+ wait_clear_urbs(ep);
+
+ for (i = 0; i < ep->nurbs; i++)
+ release_urb_ctx(&ep->urb[i]);
+
+ if (ep->syncbuf)
+ usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+ ep->syncbuf, ep->sync_dma);
+
+ ep->syncbuf = NULL;
+ ep->nurbs = 0;
+}
/*
- * initialize a substream for plaback/capture
+ * configure a data endpoint
*/
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
- unsigned int period_bytes,
- unsigned int rate,
- unsigned int frame_bits)
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+ struct snd_pcm_hw_params *hw_params,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep)
{
- unsigned int maxsize, i;
- int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- unsigned int urb_packs, total_packs, packs_per_ms;
- struct snd_usb_audio *chip = subs->stream->chip;
+ unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+ int period_bytes = params_period_bytes(hw_params);
+ int format = params_format(hw_params);
+ int is_playback = usb_pipeout(ep->pipe);
+ int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+ params_channels(hw_params);
+
+ ep->datainterval = fmt->datainterval;
+ ep->stride = frame_bits >> 3;
+ ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
- /* calculate the frequency in 16.16 format */
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
- subs->freqn = get_usb_full_speed_rate(rate);
- else
- subs->freqn = get_usb_high_speed_rate(rate);
- subs->freqm = subs->freqn;
- subs->freqshift = INT_MIN;
/* calculate max. frequency */
- if (subs->maxpacksize) {
+ if (ep->maxpacksize) {
/* whatever fits into a max. size packet */
- maxsize = subs->maxpacksize;
- subs->freqmax = (maxsize / (frame_bits >> 3))
- << (16 - subs->datainterval);
+ maxsize = ep->maxpacksize;
+ ep->freqmax = (maxsize / (frame_bits >> 3))
+ << (16 - ep->datainterval);
} else {
/* no max. packet size: just take 25% higher than nominal */
- subs->freqmax = subs->freqn + (subs->freqn >> 2);
- maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
- >> (16 - subs->datainterval);
+ ep->freqmax = ep->freqn + (ep->freqn >> 2);
+ maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+ >> (16 - ep->datainterval);
}
- subs->phase = 0;
- if (subs->fill_max)
- subs->curpacksize = subs->maxpacksize;
+ if (ep->fill_max)
+ ep->curpacksize = ep->maxpacksize;
else
- subs->curpacksize = maxsize;
+ ep->curpacksize = maxsize;
- if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
- packs_per_ms = 8 >> subs->datainterval;
+ if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+ packs_per_ms = 8 >> ep->datainterval;
else
packs_per_ms = 1;
- if (is_playback) {
- urb_packs = max(chip->nrpacks, 1);
- urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
- } else
+ if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+ urb_packs = max(ep->chip->nrpacks, 1);
+ urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+ } else {
urb_packs = 1;
+ }
+
urb_packs *= packs_per_ms;
- if (subs->syncpipe)
- urb_packs = min(urb_packs, 1U << subs->syncinterval);
+
+ if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+ urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
/* decide how many packets to be used */
- if (is_playback) {
+ if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
unsigned int minsize, maxpacks;
/* determine how small a packet can be */
- minsize = (subs->freqn >> (16 - subs->datainterval))
+ minsize = (ep->freqn >> (16 - ep->datainterval))
* (frame_bits >> 3);
/* with sync from device, assume it can be 12% lower */
- if (subs->syncpipe)
+ if (sync_ep)
minsize -= minsize >> 3;
minsize = max(minsize, 1u);
total_packs = (period_bytes + minsize - 1) / minsize;
@@ -284,284 +655,472 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
urb_packs >>= 1;
total_packs = MAX_URBS * urb_packs;
}
- subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
- if (subs->nurbs > MAX_URBS) {
+
+ ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+ if (ep->nurbs > MAX_URBS) {
/* too much... */
- subs->nurbs = MAX_URBS;
+ ep->nurbs = MAX_URBS;
total_packs = MAX_URBS * urb_packs;
- } else if (subs->nurbs < 2) {
+ } else if (ep->nurbs < 2) {
/* too little - we need at least two packets
* to ensure contiguous playback/capture
*/
- subs->nurbs = 2;
+ ep->nurbs = 2;
}
/* allocate and initialize data urbs */
- for (i = 0; i < subs->nurbs; i++) {
- struct snd_urb_ctx *u = &subs->dataurb[i];
+ for (i = 0; i < ep->nurbs; i++) {
+ struct snd_urb_ctx *u = &ep->urb[i];
u->index = i;
- u->subs = subs;
- u->packets = (i + 1) * total_packs / subs->nurbs
- - i * total_packs / subs->nurbs;
+ u->ep = ep;
+ u->packets = (i + 1) * total_packs / ep->nurbs
+ - i * total_packs / ep->nurbs;
u->buffer_size = maxsize * u->packets;
- if (subs->fmt_type == UAC_FORMAT_TYPE_II)
+
+ if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)
goto out_of_memory;
+
u->urb->transfer_buffer =
- usb_alloc_coherent(subs->dev, u->buffer_size,
+ usb_alloc_coherent(ep->chip->dev, u->buffer_size,
GFP_KERNEL, &u->urb->transfer_dma);
if (!u->urb->transfer_buffer)
goto out_of_memory;
- u->urb->pipe = subs->datapipe;
+ u->urb->pipe = ep->pipe;
u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- u->urb->interval = 1 << subs->datainterval;
+ u->urb->interval = 1 << ep->datainterval;
u->urb->context = u;
u->urb->complete = snd_complete_urb;
+ INIT_LIST_HEAD(&u->ready_list);
}
- if (subs->syncpipe) {
- /* allocate and initialize sync urbs */
- subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
- GFP_KERNEL, &subs->sync_dma);
- if (!subs->syncbuf)
- goto out_of_memory;
- for (i = 0; i < SYNC_URBS; i++) {
- struct snd_urb_ctx *u = &subs->syncurb[i];
- u->index = i;
- u->subs = subs;
- u->packets = 1;
- u->urb = usb_alloc_urb(1, GFP_KERNEL);
- if (!u->urb)
- goto out_of_memory;
- u->urb->transfer_buffer = subs->syncbuf + i * 4;
- u->urb->transfer_dma = subs->sync_dma + i * 4;
- u->urb->transfer_buffer_length = 4;
- u->urb->pipe = subs->syncpipe;
- u->urb->transfer_flags = URB_ISO_ASAP |
- URB_NO_TRANSFER_DMA_MAP;
- u->urb->number_of_packets = 1;
- u->urb->interval = 1 << subs->syncinterval;
- u->urb->context = u;
- u->urb->complete = snd_complete_sync_urb;
- }
- }
return 0;
out_of_memory:
- snd_usb_release_substream_urbs(subs, 0);
+ release_urbs(ep, 0);
return -ENOMEM;
}
/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
+ * configure a sync endpoint
*/
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+ struct snd_pcm_hw_params *hw_params,
+ struct audioformat *fmt)
{
- unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = urb->context;
+ int i;
+
+ ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+ GFP_KERNEL, &ep->sync_dma);
+ if (!ep->syncbuf)
+ return -ENOMEM;
+
+ for (i = 0; i < SYNC_URBS; i++) {
+ struct snd_urb_ctx *u = &ep->urb[i];
+ u->index = i;
+ u->ep = ep;
+ u->packets = 1;
+ u->urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!u->urb)
+ goto out_of_memory;
+ u->urb->transfer_buffer = ep->syncbuf + i * 4;
+ u->urb->transfer_dma = ep->sync_dma + i * 4;
+ u->urb->transfer_buffer_length = 4;
+ u->urb->pipe = ep->pipe;
+ u->urb->transfer_flags = URB_ISO_ASAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ u->urb->number_of_packets = 1;
+ u->urb->interval = 1 << ep->syncinterval;
+ u->urb->context = u;
+ u->urb->complete = snd_complete_urb;
+ }
+
+ ep->nurbs = SYNC_URBS;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 3;
- urb->iso_frame_desc[0].offset = 0;
- cp[0] = subs->freqn >> 2;
- cp[1] = subs->freqn >> 10;
- cp[2] = subs->freqn >> 18;
return 0;
+
+out_of_memory:
+ release_urbs(ep, 0);
+ return -ENOMEM;
}
-/*
- * prepare urb for high speed capture sync pipe
+/**
+ * snd_usb_endpoint_set_params: configure an snd_usb_endpoint
+ *
+ * @ep: the snd_usb_endpoint to configure
+ * @hw_params: the hardware parameters
+ * @fmt: the USB audio format information
+ * @sync_ep: the sync endpoint to use, if any
*
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ * Determine the number of URBs to be used on this endpoint.
+ * An endpoint must be configured before it can be started.
+ * An endpoint that is already running can not be reconfigured.
*/
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+ struct snd_pcm_hw_params *hw_params,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep)
{
- unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = urb->context;
+ int err;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 4;
- urb->iso_frame_desc[0].offset = 0;
- cp[0] = subs->freqn;
- cp[1] = subs->freqn >> 8;
- cp[2] = subs->freqn >> 16;
- cp[3] = subs->freqn >> 24;
- return 0;
+ if (ep->use_count != 0) {
+ snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+ ep->ep_num);
+ return -EBUSY;
+ }
+
+ /* release old buffers, if any */
+ release_urbs(ep, 0);
+
+ ep->datainterval = fmt->datainterval;
+ ep->maxpacksize = fmt->maxpacksize;
+ ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
+
+ if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+ ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+ else
+ ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+ /* calculate the frequency in 16.16 format */
+ ep->freqm = ep->freqn;
+ ep->freqshift = INT_MIN;
+
+ ep->phase = 0;
+
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+ break;
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ err = sync_ep_set_params(ep, hw_params, fmt);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+ ep->ep_num, ep->type, ep->nurbs, err);
+
+ return err;
}
-/*
- * process after capture sync complete
- * - nothing to do
+/**
+ * snd_usb_endpoint_start: start an snd_usb_endpoint
+ *
+ * @ep: the endpoint to start
+ *
+ * A call to this function will increment the use count of the endpoint.
+ * In case it is not already running, the URBs for this endpoint will be
+ * submitted. Otherwise, this function does nothing.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_stop().
+ *
+ * Returns an error if the URB submission failed, 0 in all other cases.
*/
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
+ int err;
+ unsigned int i;
+
+ if (ep->chip->shutdown)
+ return -EBADFD;
+
+ /* already running? */
+ if (++ep->use_count != 1)
+ return 0;
+
+ if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+ return -EINVAL;
+
+ /* just to be sure */
+ deactivate_urbs(ep, 0, 1);
+ wait_clear_urbs(ep);
+
+ ep->active_mask = 0;
+ ep->unlink_mask = 0;
+ ep->phase = 0;
+
+ /*
+ * If this endpoint has a data endpoint as implicit feedback source,
+ * don't start the urbs here. Instead, mark them all as available,
+ * wait for the record urbs to return and queue the playback urbs
+ * from that context.
+ */
+
+ set_bit(EP_FLAG_RUNNING, &ep->flags);
+
+ if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+ for (i = 0; i < ep->nurbs; i++) {
+ struct snd_urb_ctx *ctx = ep->urb + i;
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ }
+
+ return 0;
+ }
+
+ for (i = 0; i < ep->nurbs; i++) {
+ struct urb *urb = ep->urb[i].urb;
+
+ if (snd_BUG_ON(!urb))
+ goto __error;
+
+ if (usb_pipeout(ep->pipe)) {
+ prepare_outbound_urb_sizes(ep, urb->context);
+ prepare_outbound_urb(ep, urb->context);
+ } else {
+ prepare_inbound_urb(ep, urb->context);
+ }
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+ i, err, usb_error_string(err));
+ goto __error;
+ }
+ set_bit(i, &ep->active_mask);
+ }
+
return 0;
+
+__error:
+ clear_bit(EP_FLAG_RUNNING, &ep->flags);
+ ep->use_count--;
+ deactivate_urbs(ep, 0, 0);
+ return -EPIPE;
}
-/*
- * prepare urb for capture data pipe
+/**
+ * snd_usb_endpoint_stop: stop an snd_usb_endpoint
+ *
+ * @ep: the endpoint to stop (may be NULL)
*
- * fill the offset and length of each descriptor.
+ * A call to this function will decrement the use count of the endpoint.
+ * In case the last user has requested the endpoint stop, the URBs will
+ * actually be deactivated.
*
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly... the data is thus copied
- * later at complete callback to the global buffer.
+ * Must be balanced to calls of snd_usb_endpoint_start().
*/
-static int prepare_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+ int force, int can_sleep, int wait)
{
- int i, offs;
- struct snd_urb_ctx *ctx = urb->context;
+ if (!ep)
+ return;
- offs = 0;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- for (i = 0; i < ctx->packets; i++) {
- urb->iso_frame_desc[i].offset = offs;
- urb->iso_frame_desc[i].length = subs->curpacksize;
- offs += subs->curpacksize;
+ if (snd_BUG_ON(ep->use_count == 0))
+ return;
+
+ if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+ return;
+
+ if (--ep->use_count == 0) {
+ deactivate_urbs(ep, force, can_sleep);
+ ep->data_subs = NULL;
+ ep->sync_slave = NULL;
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
+ if (wait)
+ wait_clear_urbs(ep);
}
- urb->transfer_buffer_length = offs;
- urb->number_of_packets = ctx->packets;
- return 0;
}
-/*
- * process after capture complete
+/**
+ * snd_usb_endpoint_activate: activate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to activate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * correct alternate interface setting for the interface of this endpoint.
*
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
*/
-static int retire_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
{
- unsigned long flags;
- unsigned char *cp;
- int i;
- unsigned int stride, frames, bytes, oldptr;
- int period_elapsed = 0;
+ if (ep->use_count != 0)
+ return 0;
- stride = runtime->frame_bits >> 3;
+ if (!ep->chip->shutdown &&
+ !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+ int ret;
- for (i = 0; i < urb->number_of_packets; i++) {
- cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
- snd_printdd("frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
- // continue;
- }
- bytes = urb->iso_frame_desc[i].actual_length;
- frames = bytes / stride;
- if (!subs->txfr_quirk)
- bytes = frames * stride;
- if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int oldbytes = bytes;
-#endif
- bytes = frames * stride;
- snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
- oldbytes, bytes);
- }
- /* update the current pointer */
- spin_lock_irqsave(&subs->lock, flags);
- oldptr = subs->hwptr_done;
- subs->hwptr_done += bytes;
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
- frames = (bytes + (oldptr % stride)) / stride;
- subs->transfer_done += frames;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- period_elapsed = 1;
- }
- spin_unlock_irqrestore(&subs->lock, flags);
- /* copy a data chunk */
- if (oldptr + bytes > runtime->buffer_size * stride) {
- unsigned int bytes1 =
- runtime->buffer_size * stride - oldptr;
- memcpy(runtime->dma_area + oldptr, cp, bytes1);
- memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
- } else {
- memcpy(runtime->dma_area + oldptr, cp, bytes);
+ ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+ __func__, ret);
+ clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+ return ret;
}
+
+ return 0;
}
- if (period_elapsed)
- snd_pcm_period_elapsed(subs->pcm_substream);
- return 0;
+
+ return -EBUSY;
}
-/*
- * Process after capture complete when paused. Nothing to do.
+/**
+ * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to deactivate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * alternate interface setting 0 for the interface of this endpoint.
+ *
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
*/
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
{
- return 0;
-}
+ if (!ep)
+ return -EINVAL;
+ if (ep->use_count != 0)
+ return 0;
-/*
- * prepare urb for playback sync pipe
+ if (!ep->chip->shutdown &&
+ test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+ int ret;
+
+ ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+/**
+ * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
+ *
+ * @ep: the list header of the endpoint to free
*
- * set up the offset and length to receive the current frequency.
+ * This function does not care for the endpoint's use count but will tear
+ * down all the streaming URBs immediately and free all resources.
*/
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+void snd_usb_endpoint_free(struct list_head *head)
{
- struct snd_urb_ctx *ctx = urb->context;
+ struct snd_usb_endpoint *ep;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
- urb->iso_frame_desc[0].offset = 0;
- return 0;
+ ep = list_entry(head, struct snd_usb_endpoint, list);
+ release_urbs(ep, 1);
+ kfree(ep);
}
-/*
- * process after playback sync complete
- *
- * Full speed devices report feedback values in 10.14 format as samples per
- * frame, high speed devices in 16.16 format as samples per microframe.
- * Because the Audio Class 1 spec was written before USB 2.0, many high speed
- * devices use a wrong interpretation, some others use an entirely different
- * format. Therefore, we cannot predict what format any particular device uses
- * and must detect it automatically.
+/**
+ * snd_usb_handle_sync_urb: parse an USB sync packet
+ *
+ * @ep: the endpoint to handle the packet
+ * @sender: the sending endpoint
+ * @urb: the received packet
+ *
+ * This function is called from the context of an endpoint that received
+ * the packet and is used to let another endpoint object handle the payload.
*/
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+ struct snd_usb_endpoint *sender,
+ const struct urb *urb)
{
- unsigned int f;
int shift;
+ unsigned int f;
unsigned long flags;
+ snd_BUG_ON(ep == sender);
+
+ /*
+ * In case the endpoint is operating in implicit feedback mode, prepare
+ * a new outbound URB that has the same layout as the received packet
+ * and add it to the list of pending urbs. queue_pending_output_urbs()
+ * will take care of them later.
+ */
+ if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+ ep->use_count != 0) {
+
+ /* implicit feedback case */
+ int i, bytes = 0;
+ struct snd_urb_ctx *in_ctx;
+ struct snd_usb_packet_info *out_packet;
+
+ in_ctx = urb->context;
+
+ /* Count overall packet size */
+ for (i = 0; i < in_ctx->packets; i++)
+ if (urb->iso_frame_desc[i].status == 0)
+ bytes += urb->iso_frame_desc[i].actual_length;
+
+ /*
+ * skip empty packets. At least M-Audio's Fast Track Ultra stops
+ * streaming once it received a 0-byte OUT URB
+ */
+ if (bytes == 0)
+ return;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+ /*
+ * Iterate through the inbound packet and prepare the lengths
+ * for the output packet. The OUT packet we are about to send
+ * will have the same amount of payload bytes than the IN
+ * packet we just received.
+ */
+
+ out_packet->packets = in_ctx->packets;
+ for (i = 0; i < in_ctx->packets; i++) {
+ if (urb->iso_frame_desc[i].status == 0)
+ out_packet->packet_size[i] =
+ urb->iso_frame_desc[i].actual_length / ep->stride;
+ else
+ out_packet->packet_size[i] = 0;
+ }
+
+ ep->next_packet_write_pos++;
+ ep->next_packet_write_pos %= MAX_URBS;
+ spin_unlock_irqrestore(&ep->lock, flags);
+ queue_pending_output_urbs(ep);
+
+ return;
+ }
+
+ /*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples
+ * per frame, high speed devices in 16.16 format as samples per
+ * microframe.
+ *
+ * Because the Audio Class 1 spec was written before USB 2.0, many high
+ * speed devices use a wrong interpretation, some others use an
+ * entirely different format.
+ *
+ * Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+
if (urb->iso_frame_desc[0].status != 0 ||
urb->iso_frame_desc[0].actual_length < 3)
- return 0;
+ return;
f = le32_to_cpup(urb->transfer_buffer);
if (urb->iso_frame_desc[0].actual_length == 3)
f &= 0x00ffffff;
else
f &= 0x0fffffff;
+
if (f == 0)
- return 0;
+ return;
- if (unlikely(subs->freqshift == INT_MIN)) {
+ if (unlikely(ep->freqshift == INT_MIN)) {
/*
* The first time we see a feedback value, determine its format
* by shifting it left or right until it matches the nominal
@@ -569,398 +1128,34 @@ static int retire_playback_sync_urb(struct snd_usb_substream *subs,
* differ from the nominal value more than +50% or -25%.
*/
shift = 0;
- while (f < subs->freqn - subs->freqn / 4) {
+ while (f < ep->freqn - ep->freqn / 4) {
f <<= 1;
shift++;
}
- while (f > subs->freqn + subs->freqn / 2) {
+ while (f > ep->freqn + ep->freqn / 2) {
f >>= 1;
shift--;
}
- subs->freqshift = shift;
- }
- else if (subs->freqshift >= 0)
- f <<= subs->freqshift;
+ ep->freqshift = shift;
+ } else if (ep->freqshift >= 0)
+ f <<= ep->freqshift;
else
- f >>= -subs->freqshift;
+ f >>= -ep->freqshift;
- if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
+ if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
/*
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
- spin_lock_irqsave(&subs->lock, flags);
- subs->freqm = f;
- spin_unlock_irqrestore(&subs->lock, flags);
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->freqm = f;
+ spin_unlock_irqrestore(&ep->lock, flags);
} else {
/*
* Out of range; maybe the shift value is wrong.
* Reset it so that we autodetect again the next time.
*/
- subs->freqshift = INT_MIN;
- }
-
- return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
- if (subs->fill_max)
- return subs->maxframesize;
- else {
- subs->phase = (subs->phase & 0xffff)
- + (subs->freqm << subs->datainterval);
- return min(subs->phase >> 16, subs->maxframesize);
+ ep->freqshift = INT_MIN;
}
}
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned int i, offs, counts;
- struct snd_urb_ctx *ctx = urb->context;
- int stride = runtime->frame_bits >> 3;
-
- offs = 0;
- urb->dev = ctx->subs->dev;
- for (i = 0; i < ctx->packets; ++i) {
- counts = snd_usb_audio_next_packet_size(subs);
- urb->iso_frame_desc[i].offset = offs * stride;
- urb->iso_frame_desc[i].length = counts * stride;
- offs += counts;
- }
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * stride;
- memset(urb->transfer_buffer,
- runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
- offs * stride);
- return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- int i, stride;
- unsigned int counts, frames, bytes;
- unsigned long flags;
- int period_elapsed = 0;
- struct snd_urb_ctx *ctx = urb->context;
-
- stride = runtime->frame_bits >> 3;
-
- frames = 0;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->number_of_packets = 0;
- spin_lock_irqsave(&subs->lock, flags);
- for (i = 0; i < ctx->packets; i++) {
- counts = snd_usb_audio_next_packet_size(subs);
- /* set up descriptor */
- urb->iso_frame_desc[i].offset = frames * stride;
- urb->iso_frame_desc[i].length = counts * stride;
- frames += counts;
- urb->number_of_packets++;
- subs->transfer_done += counts;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- period_elapsed = 1;
- if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
- if (subs->transfer_done > 0) {
- /* FIXME: fill-max mode is not
- * supported yet */
- frames -= subs->transfer_done;
- counts -= subs->transfer_done;
- urb->iso_frame_desc[i].length =
- counts * stride;
- subs->transfer_done = 0;
- }
- i++;
- if (i < ctx->packets) {
- /* add a transfer delimiter */
- urb->iso_frame_desc[i].offset =
- frames * stride;
- urb->iso_frame_desc[i].length = 0;
- urb->number_of_packets++;
- }
- break;
- }
- }
- if (period_elapsed) /* finish at the period boundary */
- break;
- }
- bytes = frames * stride;
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
- } else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
- }
- subs->hwptr_done += bytes;
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
-
- /* update delay with exact number of samples queued */
- runtime->delay = subs->last_delay;
- runtime->delay += frames;
- subs->last_delay = runtime->delay;
-
- /* realign last_frame_number */
- subs->last_frame_number = usb_get_current_frame_number(subs->dev);
- subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
- spin_unlock_irqrestore(&subs->lock, flags);
- urb->transfer_buffer_length = bytes;
- if (period_elapsed)
- snd_pcm_period_elapsed(subs->pcm_substream);
- return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned long flags;
- int stride = runtime->frame_bits >> 3;
- int processed = urb->transfer_buffer_length / stride;
- int est_delay;
-
- spin_lock_irqsave(&subs->lock, flags);
-
- est_delay = snd_usb_pcm_delay(subs, runtime->rate);
- /* update delay with exact number of samples played */
- if (processed > subs->last_delay)
- subs->last_delay = 0;
- else
- subs->last_delay -= processed;
- runtime->delay = subs->last_delay;
-
- /*
- * Report when delay estimate is off by more than 2ms.
- * The error should be lower than 2ms since the estimate relies
- * on two reads of a counter updated every ms.
- */
- if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
- snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
- est_delay, subs->last_delay);
-
- spin_unlock_irqrestore(&subs->lock, flags);
- return 0;
-}
-
-static const char *usb_error_string(int err)
-{
- switch (err) {
- case -ENODEV:
- return "no device";
- case -ENOENT:
- return "endpoint not enabled";
- case -EPIPE:
- return "endpoint stalled";
- case -ENOSPC:
- return "not enough bandwidth";
- case -ESHUTDOWN:
- return "device disabled";
- case -EHOSTUNREACH:
- return "device suspended";
- case -EINVAL:
- case -EAGAIN:
- case -EFBIG:
- case -EMSGSIZE:
- return "internal error";
- default:
- return "unknown error";
- }
-}
-
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
- unsigned int i;
- int err;
-
- if (subs->stream->chip->shutdown)
- return -EBADFD;
-
- for (i = 0; i < subs->nurbs; i++) {
- if (snd_BUG_ON(!subs->dataurb[i].urb))
- return -EINVAL;
- if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
- snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
- goto __error;
- }
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (snd_BUG_ON(!subs->syncurb[i].urb))
- return -EINVAL;
- if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
- snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
- goto __error;
- }
- }
- }
-
- subs->active_mask = 0;
- subs->unlink_mask = 0;
- subs->running = 1;
- for (i = 0; i < subs->nurbs; i++) {
- err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
- if (err < 0) {
- snd_printk(KERN_ERR "cannot submit datapipe "
- "for urb %d, error %d: %s\n",
- i, err, usb_error_string(err));
- goto __error;
- }
- set_bit(i, &subs->active_mask);
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
- if (err < 0) {
- snd_printk(KERN_ERR "cannot submit syncpipe "
- "for urb %d, error %d: %s\n",
- i, err, usb_error_string(err));
- goto __error;
- }
- set_bit(i + 16, &subs->active_mask);
- }
- }
- return 0;
-
- __error:
- // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
- deactivate_urbs(subs, 0, 0);
- return -EPIPE;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
- {
- .prepare = prepare_nodata_playback_urb,
- .retire = retire_playback_urb,
- .prepare_sync = prepare_playback_sync_urb,
- .retire_sync = retire_playback_sync_urb,
- },
- {
- .prepare = prepare_capture_urb,
- .retire = retire_capture_urb,
- .prepare_sync = prepare_capture_sync_urb,
- .retire_sync = retire_capture_sync_urb,
- },
-};
-
-/*
- * initialize the substream instance.
- */
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
- int stream, struct audioformat *fp)
-{
- struct snd_usb_substream *subs = &as->substream[stream];
-
- INIT_LIST_HEAD(&subs->fmt_list);
- spin_lock_init(&subs->lock);
-
- subs->stream = as;
- subs->direction = stream;
- subs->dev = as->chip->dev;
- subs->txfr_quirk = as->chip->txfr_quirk;
- subs->ops = audio_urb_ops[stream];
- if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
- subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
-
- snd_usb_set_pcm_ops(as->pcm, stream);
-
- list_add_tail(&fp->list, &subs->fmt_list);
- subs->formats |= fp->formats;
- subs->endpoint = fp->endpoint;
- subs->num_formats++;
- subs->fmt_type = fp->fmt_type;
-}
-
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_usb_substream *subs = substream->runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- subs->ops.prepare = prepare_playback_urb;
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- return deactivate_urbs(subs, 0, 0);
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- subs->ops.prepare = prepare_nodata_playback_urb;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_usb_substream *subs = substream->runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- subs->ops.retire = retire_capture_urb;
- return start_urbs(subs, substream->runtime);
- case SNDRV_PCM_TRIGGER_STOP:
- return deactivate_urbs(subs, 0, 0);
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- subs->ops.retire = retire_paused_capture_urb;
- return 0;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- subs->ops.retire = retire_capture_urb;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime)
-{
- /* clear urbs (to be sure) */
- deactivate_urbs(subs, 0, 1);
- wait_clear_urbs(subs);
-
- /* for playback, submit the URBs now; otherwise, the first hwptr_done
- * updates for all URBs would happen at the same time when starting */
- if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
- subs->ops.prepare = prepare_nodata_playback_urb;
- return start_urbs(subs, runtime);
- }
-
- return 0;
-}
-
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a636e..ee2723fb174 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -1,21 +1,29 @@
#ifndef __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
-void snd_usb_init_substream(struct snd_usb_stream *as,
- int stream,
- struct audioformat *fp);
+#define SND_USB_ENDPOINT_TYPE_DATA 0
+#define SND_USB_ENDPOINT_TYPE_SYNC 1
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
- unsigned int period_bytes,
- unsigned int rate,
- unsigned int frame_bits);
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int ep_num, int direction, int type);
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+ struct snd_pcm_hw_params *hw_params,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep);
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime);
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+ int force, int can_sleep, int wait);
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+ struct snd_usb_endpoint *sender,
+ const struct urb *urb);
#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index ab23869c01b..4f40ba82316 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -486,7 +486,7 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
/*
* TLV callback for mixer volume controls
*/
-static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
@@ -770,6 +770,26 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
switch (cval->mixer->chip->usb_id) {
+ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+ if (strcmp(kctl->id.name, "Effect Duration") == 0) {
+ snd_printk(KERN_INFO
+ "usb-audio: set quirk for FTU Effect Duration\n");
+ cval->min = 0x0000;
+ cval->max = 0x7f00;
+ cval->res = 0x0100;
+ break;
+ }
+ if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
+ strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
+ snd_printk(KERN_INFO
+ "usb-audio: set quirks for FTU Effect Feedback/Volume\n");
+ cval->min = 0x00;
+ cval->max = 0x7f;
+ break;
+ }
+ break;
+
case USB_ID(0x0471, 0x0101):
case USB_ID(0x0471, 0x0104):
case USB_ID(0x0471, 0x0105):
@@ -1121,9 +1141,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
len = snd_usb_copy_string_desc(state, nameid,
kctl->id.name, sizeof(kctl->id.name));
- /* get min/max values */
- get_min_max_with_quirks(cval, 0, kctl);
-
switch (control) {
case UAC_FU_MUTE:
case UAC_FU_VOLUME:
@@ -1155,17 +1172,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
append_ctl_name(kctl, control == UAC_FU_MUTE ?
" Switch" : " Volume");
- if (control == UAC_FU_VOLUME) {
- check_mapped_dB(map, cval);
- if (cval->dBmin < cval->dBmax || !cval->initialized) {
- kctl->tlv.c = mixer_vol_tlv;
- kctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
- }
- }
break;
-
default:
if (! len)
strlcpy(kctl->id.name, audio_feature_info[control-1].name,
@@ -1173,6 +1180,19 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
break;
}
+ /* get min/max values */
+ get_min_max_with_quirks(cval, 0, kctl);
+
+ if (control == UAC_FU_VOLUME) {
+ check_mapped_dB(map, cval);
+ if (cval->dBmin < cval->dBmax || !cval->initialized) {
+ kctl->tlv.c = snd_usb_mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
+ }
+
range = (cval->max - cval->min) / cval->res;
/* Are there devices with volume range more than 255? I use a bit more
* to be sure. 384 is a resolution magic number found on Logitech
@@ -1388,7 +1408,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
for (pin = 0; pin < input_pins; pin++) {
err = parse_audio_unit(state, desc->baSourceID[pin]);
if (err < 0)
- return err;
+ continue;
err = check_input_term(state, desc->baSourceID[pin], &iterm);
if (err < 0)
return err;
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 81b2d8a32fb..a7f3d45a8ac 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -68,4 +68,7 @@ int snd_usb_mixer_activate(struct usb_mixer_interface *mixer);
int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl);
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv);
+
#endif /* __USBMIXER_H */
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index f1324c42383..41daaa24c25 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -288,6 +288,15 @@ static struct usbmix_name_map scratch_live_map[] = {
{ 0 } /* terminator */
};
+static struct usbmix_name_map ebox44_map[] = {
+ { 4, NULL }, /* FU */
+ { 6, NULL }, /* MU */
+ { 7, NULL }, /* FU */
+ { 10, NULL }, /* FU */
+ { 11, NULL }, /* MU */
+ { 0 }
+};
+
/* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+"
* most importand difference is SU[8], it should be set to "Capture Source"
* to make alsamixer and PA working properly.
@@ -371,6 +380,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = scratch_live_map,
.ignore_ctl_error = 1,
},
+ {
+ .id = USB_ID(0x200c, 0x1018),
+ .map = ebox44_map,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index ab125ee0b0f..41f4b691192 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -42,6 +42,77 @@
extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
+/* private_free callback */
+static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+ kfree(kctl->private_data);
+ kctl->private_data = NULL;
+}
+
+/* This function allows for the creation of standard UAC controls.
+ * See the quirks for M-Audio FTUs or Ebox-44.
+ * If you don't want to set a TLV callback pass NULL.
+ *
+ * Since there doesn't seem to be a devices that needs a multichannel
+ * version, we keep it mono for simplicity.
+ */
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+ int err;
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ cval->id = unitid;
+ cval->mixer = mixer;
+ cval->val_type = val_type;
+ cval->channels = 1;
+ cval->control = control;
+ cval->cmask = cmask;
+
+ /* get_min_max() is called only for integer volumes later,
+ * so provide a short-cut for booleans */
+ cval->min = 0;
+ cval->max = 1;
+ cval->res = 0;
+ cval->dBmin = 0;
+ cval->dBmax = 0;
+
+ /* Create control */
+ kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ /* Set name */
+ snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+ kctl->private_free = usb_mixer_elem_free;
+
+ /* set TLV */
+ if (tlv_callback) {
+ kctl->tlv.c = tlv_callback;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
+ /* Add control to mixer */
+ err = snd_usb_mixer_add_control(mixer, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
/*
* Sound Blaster remote control configuration
*
@@ -495,60 +566,218 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
}
/* M-Audio FastTrack Ultra quirks */
+/* FTU Effect switch */
+struct snd_ftu_eff_switch_priv_val {
+ struct usb_mixer_interface *mixer;
+ int cached_value;
+ int is_cached;
+};
-/* private_free callback */
-static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- kfree(kctl->private_data);
- kctl->private_data = NULL;
+ static const char *texts[8] = {"Room 1",
+ "Room 2",
+ "Room 3",
+ "Hall 1",
+ "Hall 2",
+ "Plate",
+ "Delay",
+ "Echo"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 8;
+ if (uinfo->value.enumerated.item > 7)
+ uinfo->value.enumerated.item = 7;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
}
-static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer,
- int in, int out, const char *name)
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval;
+ struct snd_usb_audio *chip;
+ struct usb_mixer_interface *mixer;
+ struct snd_ftu_eff_switch_priv_val *pval;
+ int err;
+ unsigned char value[2];
+
+ const int id = 6;
+ const int validx = 1;
+ const int val_len = 2;
+
+ value[0] = 0x00;
+ value[1] = 0x00;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+
+ if (pval->is_cached) {
+ ucontrol->value.enumerated.item[0] = pval->cached_value;
+ return 0;
+ }
+
+ mixer = (struct usb_mixer_interface *) pval->mixer;
+ if (snd_BUG_ON(!mixer))
+ return -EINVAL;
+
+ chip = (struct snd_usb_audio *) mixer->chip;
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
+
+
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ if (err < 0)
+ return err;
+
+ ucontrol->value.enumerated.item[0] = value[0];
+ pval->cached_value = value[0];
+ pval->is_cached = 1;
+
+ return 0;
+}
+
+static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_usb_audio *chip;
+ struct snd_ftu_eff_switch_priv_val *pval;
+
+ struct usb_mixer_interface *mixer;
+ int changed, cur_val, err, new_val;
+ unsigned char value[2];
+
+
+ const int id = 6;
+ const int validx = 1;
+ const int val_len = 2;
+
+ changed = 0;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+ cur_val = pval->cached_value;
+ new_val = ucontrol->value.enumerated.item[0];
+
+ mixer = (struct usb_mixer_interface *) pval->mixer;
+ if (snd_BUG_ON(!mixer))
+ return -EINVAL;
+
+ chip = (struct snd_usb_audio *) mixer->chip;
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
+
+ if (!pval->is_cached) {
+ /* Read current value */
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ if (err < 0)
+ return err;
+
+ cur_val = value[0];
+ pval->cached_value = cur_val;
+ pval->is_cached = 1;
+ }
+ /* update value if needed */
+ if (cur_val != new_val) {
+ value[0] = new_val;
+ value[1] = 0;
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ if (err < 0)
+ return err;
+
+ pval->cached_value = new_val;
+ pval->is_cached = 1;
+ changed = 1;
+ }
+
+ return changed;
+}
+
+static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer)
+{
+ static struct snd_kcontrol_new template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Effect Program Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ftu_eff_switch_info,
+ .get = snd_ftu_eff_switch_get,
+ .put = snd_ftu_eff_switch_put
+ };
+
+ int err;
struct snd_kcontrol *kctl;
+ struct snd_ftu_eff_switch_priv_val *pval;
- cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (!cval)
+ pval = kzalloc(sizeof(*pval), GFP_KERNEL);
+ if (!pval)
return -ENOMEM;
- cval->id = 5;
- cval->mixer = mixer;
- cval->val_type = USB_MIXER_S16;
- cval->channels = 1;
- cval->control = out + 1;
- cval->cmask = 1 << in;
+ pval->cached_value = 0;
+ pval->is_cached = 0;
+ pval->mixer = mixer;
- kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ template.private_value = (unsigned long) pval;
+ kctl = snd_ctl_new1(&template, mixer->chip);
if (!kctl) {
- kfree(cval);
+ kfree(pval);
return -ENOMEM;
}
- snprintf(kctl->id.name, sizeof(kctl->id.name), name);
- kctl->private_free = usb_mixer_elem_free;
- return snd_usb_mixer_add_control(mixer, kctl);
+ err = snd_ctl_add(mixer->chip->card, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
}
-static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
+/* Create volume controls for FTU devices*/
+static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
{
char name[64];
+ unsigned int control, cmask;
int in, out, err;
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+
for (out = 0; out < 8; out++) {
+ control = out + 1;
for (in = 0; in < 8; in++) {
+ cmask = 1 << in;
snprintf(name, sizeof(name),
- "AIn%d - Out%d Capture Volume", in + 1, out + 1);
- err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ "AIn%d - Out%d Capture Volume",
+ in + 1, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
-
for (in = 8; in < 16; in++) {
+ cmask = 1 << in;
snprintf(name, sizeof(name),
- "DIn%d - Out%d Playback Volume", in - 7, out + 1);
- err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ "DIn%d - Out%d Playback Volume",
+ in - 7, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -557,6 +786,191 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
return 0;
}
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 2;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Duration";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 3;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Feedback Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 4;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, NULL);
+}
+
+static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 7;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 7;
+
+ for (ch = 0; ch < 4; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Return %d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 9;
+
+ for (ch = 0; ch < 8; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send AIn%d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ for (ch = 8; ch < 16; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send DIn%d Volume", ch - 7);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_ftu_create_volume_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_switch(mixer);
+ if (err < 0)
+ return err;
+ err = snd_ftu_create_effect_volume_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_duration_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_feedback_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_return_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_send_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * Create mixer for Electrix Ebox-44
+ *
+ * The mixer units from this device are corrupt, and even where they
+ * are valid they presents mono controls as L and R channels of
+ * stereo. So we create a good mixer in code.
+ */
+
+static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Headphone Playback Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16,
+ "Headphone A Mix Playback Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16,
+ "Headphone B Mix Playback Volume", NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Output Playback Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16,
+ "Output A Playback Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16,
+ "Output B Playback Volume", NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Input Capture Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16,
+ "Input A Capture Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16,
+ "Input B Capture Volume", NULL);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id)
{
@@ -600,7 +1014,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
- err = snd_maudio_ftu_create_mixer(mixer);
+ err = snd_ftu_create_mixer(mixer);
break;
case USB_ID(0x0b05, 0x1739):
@@ -619,6 +1033,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
snd_nativeinstruments_ta10_mixers,
ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
break;
+
+ case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
+ err = snd_ebox44_create_mixer(mixer);
+ break;
}
return err;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 0eed6115c2d..24839d93264 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
@@ -34,6 +35,9 @@
#include "clock.h"
#include "power.h"
+#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
+#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
+
/* return the estimated delay based on USB frame counters */
snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
unsigned int rate)
@@ -208,6 +212,84 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
}
}
+static int start_endpoints(struct snd_usb_substream *subs)
+{
+ int err;
+
+ if (!subs->data_endpoint)
+ return -EINVAL;
+
+ if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
+
+ snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep);
+
+ ep->data_subs = subs;
+ err = snd_usb_endpoint_start(ep);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+ return err;
+ }
+ }
+
+ if (subs->sync_endpoint &&
+ !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+ struct snd_usb_endpoint *ep = subs->sync_endpoint;
+
+ snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep);
+
+ ep->sync_slave = subs->data_endpoint;
+ err = snd_usb_endpoint_start(ep);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void stop_endpoints(struct snd_usb_substream *subs,
+ int force, int can_sleep, int wait)
+{
+ if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags))
+ snd_usb_endpoint_stop(subs->sync_endpoint,
+ force, can_sleep, wait);
+
+ if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags))
+ snd_usb_endpoint_stop(subs->data_endpoint,
+ force, can_sleep, wait);
+}
+
+static int activate_endpoints(struct snd_usb_substream *subs)
+{
+ if (subs->sync_endpoint) {
+ int ret;
+
+ ret = snd_usb_endpoint_activate(subs->sync_endpoint);
+ if (ret < 0)
+ return ret;
+ }
+
+ return snd_usb_endpoint_activate(subs->data_endpoint);
+}
+
+static int deactivate_endpoints(struct snd_usb_substream *subs)
+{
+ int reta, retb;
+
+ reta = snd_usb_endpoint_deactivate(subs->sync_endpoint);
+ retb = snd_usb_endpoint_deactivate(subs->data_endpoint);
+
+ if (reta < 0)
+ return reta;
+
+ if (retb < 0)
+ return retb;
+
+ return 0;
+}
+
/*
* find a matching format and set up the interface
*/
@@ -219,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct usb_interface *iface;
unsigned int ep, attr;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- int err;
+ int err, implicit_fb = 0;
iface = usb_ifnum_to_if(dev, fmt->iface);
if (WARN_ON(!iface))
@@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
if (fmt == subs->cur_audiofmt)
return 0;
- /* close the old interface */
- if (subs->interface >= 0 && subs->interface != fmt->iface) {
- if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
- dev->devnum, fmt->iface, fmt->altsetting);
- return -EIO;
- }
- subs->interface = -1;
- subs->altset_idx = 0;
- }
-
- /* set interface */
- if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
- if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
- dev->devnum, fmt->iface, fmt->altsetting);
- return -EIO;
- }
- snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
- subs->interface = fmt->iface;
- subs->altset_idx = fmt->altset_idx;
- }
-
- /* create a data pipe */
- ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
- if (is_playback)
- subs->datapipe = usb_sndisocpipe(dev, ep);
- else
- subs->datapipe = usb_rcvisocpipe(dev, ep);
- subs->datainterval = fmt->datainterval;
- subs->syncpipe = subs->syncinterval = 0;
- subs->maxpacksize = fmt->maxpacksize;
- subs->syncmaxsize = 0;
- subs->fill_max = 0;
+ subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, fmt->endpoint, subs->direction,
+ SND_USB_ENDPOINT_TYPE_DATA);
+ if (!subs->data_endpoint)
+ return -EINVAL;
/* we need a sync pipe in async OUT or adaptive IN mode */
/* check the number of EP, since some devices have broken
@@ -273,8 +326,25 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
* assume it as adaptive-out or sync-in.
*/
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+ switch (subs->stream->chip->usb_id) {
+ case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+ case USB_ID(0x0763, 0x2081):
+ if (is_playback) {
+ implicit_fb = 1;
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 2);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+ }
+ }
+
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
- (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+ (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
altsd->bNumEndpoints >= 2) {
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
@@ -282,7 +352,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
the audio fields in the endpoint descriptors */
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- get_endpoint(alts, 1)->bSynchAddress != 0)) {
+ get_endpoint(alts, 1)->bSynchAddress != 0 &&
+ !implicit_fb)) {
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EINVAL;
@@ -290,33 +361,27 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
ep = get_endpoint(alts, 1)->bEndpointAddress;
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
(( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
- (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+ (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
+ ( is_playback && !implicit_fb))) {
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EINVAL;
}
- ep &= USB_ENDPOINT_NUMBER_MASK;
- if (is_playback)
- subs->syncpipe = usb_rcvisocpipe(dev, ep);
- else
- subs->syncpipe = usb_sndisocpipe(dev, ep);
- if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- get_endpoint(alts, 1)->bRefresh >= 1 &&
- get_endpoint(alts, 1)->bRefresh <= 9)
- subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
- else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
- subs->syncinterval = 1;
- else if (get_endpoint(alts, 1)->bInterval >= 1 &&
- get_endpoint(alts, 1)->bInterval <= 16)
- subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
- else
- subs->syncinterval = 3;
- subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
- }
-
- /* always fill max packet size */
- if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
- subs->fill_max = 1;
+
+ implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+ == USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+add_sync_ep:
+ subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, ep, !subs->direction,
+ implicit_fb ?
+ SND_USB_ENDPOINT_TYPE_DATA :
+ SND_USB_ENDPOINT_TYPE_SYNC);
+ if (!subs->sync_endpoint)
+ return -EINVAL;
+
+ subs->data_endpoint->sync_master = subs->sync_endpoint;
+ }
if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
return err;
@@ -390,15 +455,30 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (changed) {
mutex_lock(&subs->stream->chip->shutdown_mutex);
/* format changed */
- snd_usb_release_substream_urbs(subs, 0);
- /* influenced: period_bytes, channels, rate, format, */
- ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
- params_rate(hw_params),
- snd_pcm_format_physical_width(params_format(hw_params)) *
- params_channels(hw_params));
+ stop_endpoints(subs, 0, 0, 0);
+ deactivate_endpoints(subs);
+
+ ret = activate_endpoints(subs);
+ if (ret < 0)
+ goto unlock;
+
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
+ subs->sync_endpoint);
+ if (ret < 0)
+ goto unlock;
+
+ if (subs->sync_endpoint)
+ ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+ hw_params, fmt, NULL);
+unlock:
mutex_unlock(&subs->stream->chip->shutdown_mutex);
}
+ if (ret == 0) {
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
+ }
+
return ret;
}
@@ -415,7 +495,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs->cur_rate = 0;
subs->period_bytes = 0;
mutex_lock(&subs->stream->chip->shutdown_mutex);
- snd_usb_release_substream_urbs(subs, 0);
+ stop_endpoints(subs, 0, 1, 1);
mutex_unlock(&subs->stream->chip->shutdown_mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
@@ -435,19 +515,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
return -ENXIO;
}
+ if (snd_BUG_ON(!subs->data_endpoint))
+ return -EIO;
+
/* some unit conversions in runtime */
- subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
- subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+ subs->data_endpoint->maxframesize =
+ bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
+ subs->data_endpoint->curframesize =
+ bytes_to_frames(runtime, subs->data_endpoint->curpacksize);
/* reset the pointer */
subs->hwptr_done = 0;
subs->transfer_done = 0;
- subs->phase = 0;
subs->last_delay = 0;
subs->last_frame_number = 0;
runtime->delay = 0;
- return snd_usb_substream_prepare(subs, runtime);
+ /* for playback, submit the URBs now; otherwise, the first hwptr_done
+ * updates for all URBs would happen at the same time when starting */
+ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ return start_endpoints(subs);
+
+ return 0;
}
static struct snd_pcm_hardware snd_usb_hardware =
@@ -842,16 +931,171 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
{
+ int ret;
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_usb_substream *subs = &as->substream[direction];
- if (!as->chip->shutdown && subs->interface >= 0) {
- usb_set_interface(subs->dev, subs->interface, 0);
- subs->interface = -1;
- }
+ stop_endpoints(subs, 0, 0, 0);
+ ret = deactivate_endpoints(subs);
subs->pcm_substream = NULL;
snd_usb_autosuspend(subs->stream->chip);
- return 0;
+
+ return ret;
+}
+
+/* Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static void retire_capture_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ unsigned int stride, frames, bytes, oldptr;
+ int i, period_elapsed = 0;
+ unsigned long flags;
+ unsigned char *cp;
+
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
+ snd_printdd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+ // continue;
+ }
+ bytes = urb->iso_frame_desc[i].actual_length;
+ frames = bytes / stride;
+ if (!subs->txfr_quirk)
+ bytes = frames * stride;
+ if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int oldbytes = bytes;
+#endif
+ bytes = frames * stride;
+ snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+ oldbytes, bytes);
+ }
+ /* update the current pointer */
+ spin_lock_irqsave(&subs->lock, flags);
+ oldptr = subs->hwptr_done;
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+ frames = (bytes + (oldptr % stride)) / stride;
+ subs->transfer_done += frames;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ }
+ spin_unlock_irqrestore(&subs->lock, flags);
+ /* copy a data chunk */
+ if (oldptr + bytes > runtime->buffer_size * stride) {
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - oldptr;
+ memcpy(runtime->dma_area + oldptr, cp, bytes1);
+ memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+ } else {
+ memcpy(runtime->dma_area + oldptr, cp, bytes);
+ }
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+static void prepare_playback_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ struct snd_urb_ctx *ctx = urb->context;
+ unsigned int counts, frames, bytes;
+ int i, stride, period_elapsed = 0;
+ unsigned long flags;
+
+ stride = runtime->frame_bits >> 3;
+
+ frames = 0;
+ urb->number_of_packets = 0;
+ spin_lock_irqsave(&subs->lock, flags);
+ for (i = 0; i < ctx->packets; i++) {
+ counts = ctx->packet_size[i];
+ /* set up descriptor */
+ urb->iso_frame_desc[i].offset = frames * stride;
+ urb->iso_frame_desc[i].length = counts * stride;
+ frames += counts;
+ urb->number_of_packets++;
+ subs->transfer_done += counts;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+ if (subs->transfer_done > 0) {
+ /* FIXME: fill-max mode is not
+ * supported yet */
+ frames -= subs->transfer_done;
+ counts -= subs->transfer_done;
+ urb->iso_frame_desc[i].length =
+ counts * stride;
+ subs->transfer_done = 0;
+ }
+ i++;
+ if (i < ctx->packets) {
+ /* add a transfer delimiter */
+ urb->iso_frame_desc[i].offset =
+ frames * stride;
+ urb->iso_frame_desc[i].length = 0;
+ urb->number_of_packets++;
+ }
+ break;
+ }
+ }
+ if (period_elapsed &&
+ !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+ break;
+ }
+ bytes = frames * stride;
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+ runtime->delay += frames;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ urb->transfer_buffer_length = bytes;
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static void retire_playback_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ int stride = runtime->frame_bits >> 3;
+ int processed = urb->transfer_buffer_length / stride;
+
+ spin_lock_irqsave(&subs->lock, flags);
+ if (processed > runtime->delay)
+ runtime->delay = 0;
+ else
+ runtime->delay -= processed;
+ spin_unlock_irqrestore(&subs->lock, flags);
}
static int snd_usb_playback_open(struct snd_pcm_substream *substream)
@@ -874,6 +1118,63 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream)
return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
}
+static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
+ subs->data_endpoint->retire_data_urb = retire_playback_urb;
+ subs->running = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stop_endpoints(subs, 0, 0, 0);
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->data_endpoint->prepare_data_urb = NULL;
+ subs->data_endpoint->retire_data_urb = NULL;
+ subs->running = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int err;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = start_endpoints(subs);
+ if (err < 0)
+ return err;
+
+ subs->data_endpoint->retire_data_urb = retire_capture_urb;
+ subs->running = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stop_endpoints(subs, 0, 0, 0);
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->data_endpoint->retire_data_urb = NULL;
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->data_endpoint->retire_data_urb = retire_capture_urb;
+ subs->running = 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static struct snd_pcm_ops snd_usb_playback_ops = {
.open = snd_usb_playback_open,
.close = snd_usb_playback_close,
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index 961c9a25068..ebc1a5b5b3f 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -25,6 +25,7 @@
#include "usbaudio.h"
#include "helper.h"
#include "card.h"
+#include "endpoint.h"
#include "proc.h"
/* convert our full speed USB rate into sampling rate in Hz */
@@ -115,28 +116,33 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
}
+static void proc_dump_ep_status(struct snd_usb_substream *subs,
+ struct snd_usb_endpoint *ep,
+ struct snd_info_buffer *buffer)
+{
+ if (!ep)
+ return;
+ snd_iprintf(buffer, " Packet Size = %d\n", ep->curpacksize);
+ snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
+ snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
+ ? get_full_speed_hz(ep->freqm)
+ : get_high_speed_hz(ep->freqm),
+ ep->freqm >> 16, ep->freqm & 0xffff);
+ if (ep->freqshift != INT_MIN) {
+ int res = 16 - ep->freqshift;
+ snd_iprintf(buffer, " Feedback Format = %d.%d\n",
+ (ep->syncmaxsize > 3 ? 32 : 24) - res, res);
+ }
+}
+
static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
if (subs->running) {
- unsigned int i;
snd_iprintf(buffer, " Status: Running\n");
snd_iprintf(buffer, " Interface = %d\n", subs->interface);
snd_iprintf(buffer, " Altset = %d\n", subs->altset_idx);
- snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs);
- for (i = 0; i < subs->nurbs; i++)
- snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
- snd_iprintf(buffer, "]\n");
- snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize);
- snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
- snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
- ? get_full_speed_hz(subs->freqm)
- : get_high_speed_hz(subs->freqm),
- subs->freqm >> 16, subs->freqm & 0xffff);
- if (subs->freqshift != INT_MIN)
- snd_iprintf(buffer, " Feedback Format = %d.%d\n",
- (subs->syncmaxsize > 3 ? 32 : 24)
- - (16 - subs->freqshift),
- 16 - subs->freqshift);
+ proc_dump_ep_status(subs, subs->data_endpoint, buffer);
+ proc_dump_ep_status(subs, subs->sync_endpoint, buffer);
} else {
snd_iprintf(buffer, " Status: Stop\n");
}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 5ff8010b2d6..6b7d7a2b7ba 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
}
}
+/*
+ * initialize the substream instance.
+ */
+
+static void snd_usb_init_substream(struct snd_usb_stream *as,
+ int stream,
+ struct audioformat *fp)
+{
+ struct snd_usb_substream *subs = &as->substream[stream];
+
+ INIT_LIST_HEAD(&subs->fmt_list);
+ spin_lock_init(&subs->lock);
+
+ subs->stream = as;
+ subs->direction = stream;
+ subs->dev = as->chip->dev;
+ subs->txfr_quirk = as->chip->txfr_quirk;
+
+ snd_usb_set_pcm_ops(as->pcm, stream);
+
+ list_add_tail(&fp->list, &subs->fmt_list);
+ subs->formats |= fp->formats;
+ subs->num_formats++;
+ subs->fmt_type = fp->fmt_type;
+}
/*
* add this endpoint to the chip instance.
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
- if (!subs->endpoint)
+ if (!subs->data_endpoint)
continue;
- if (subs->endpoint == fp->endpoint) {
+ if (subs->data_endpoint->ep_num == fp->endpoint) {
list_add_tail(&fp->list, &subs->fmt_list);
subs->num_formats++;
subs->formats |= fp->formats;
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
- if (subs->endpoint)
+ if (subs->data_endpoint)
continue;
err = snd_pcm_new_stream(as->pcm, stream, 1);
if (err < 0)
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 3e2b0357793..b8233ebe250 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -36,6 +36,7 @@ struct snd_usb_audio {
struct snd_card *card;
struct usb_interface *pm_intf;
u32 usb_id;
+ struct mutex mutex;
struct mutex shutdown_mutex;
unsigned int shutdown:1;
unsigned int probing:1;
@@ -46,6 +47,7 @@ struct snd_usb_audio {
int num_suspended_intf;
struct list_head pcm_list; /* list of pcm streams */
+ struct list_head ep_list; /* list of audio-related endpoints */
int pcm_devs;
struct list_head midi_list; /* list of midi interfaces */