aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-12-26 14:52:54 +0000
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-12-26 14:52:54 +0000
commit7f50548abb5454bd82c25aae15f0a3bf6a530f46 (patch)
tree175b5d695437151f0f9f778ad8eb7f274468842f /sound
parentb3172f222ab5afdc91ea058bd11c42cf169728f3 (diff)
parent6b7b284958d47b77d06745b36bc7f36dab769d9b (diff)
downloadlinux-2.6.34-ux500-7f50548abb5454bd82c25aae15f0a3bf6a530f46.tar.gz
Merge commit 'v2.6.33-rc2' into for-2.6.33
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig6
-rw-r--r--sound/arm/Makefile2
-rw-r--r--sound/arm/aaci.c204
-rw-r--r--sound/arm/aaci.h2
-rw-r--r--sound/arm/devdma.c80
-rw-r--r--sound/arm/devdma.h3
-rw-r--r--sound/arm/pxa2xx-ac97.c2
-rw-r--r--sound/core/Kconfig1
-rw-r--r--sound/core/control.c9
-rw-r--r--sound/core/hrtimer.c15
-rw-r--r--sound/core/isadma.c10
-rw-r--r--sound/core/oss/mixer_oss.c4
-rw-r--r--sound/core/pcm.c6
-rw-r--r--sound/core/pcm_lib.c4
-rw-r--r--sound/core/pcm_native.c83
-rw-r--r--sound/core/pcm_timer.c17
-rw-r--r--sound/core/rawmidi.c61
-rw-r--r--sound/drivers/pcsp/pcsp.c32
-rw-r--r--sound/drivers/pcsp/pcsp.h2
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c35
-rw-r--r--sound/i2c/cs8427.c15
-rw-r--r--sound/i2c/other/Makefile3
-rw-r--r--sound/i2c/other/ak4113.c639
-rw-r--r--sound/i2c/other/ak4xxx-adda.c136
-rw-r--r--sound/i2c/other/tea575x-tuner.c2
-rw-r--r--sound/isa/Kconfig12
-rw-r--r--sound/isa/cmi8330.c4
-rw-r--r--sound/isa/cs423x/cs4236.c15
-rw-r--r--sound/isa/cs423x/cs4236_lib.c241
-rw-r--r--sound/isa/es1688/es1688_lib.c2
-rw-r--r--sound/isa/es18xx.c221
-rw-r--r--sound/isa/gus/gus_mem.c3
-rw-r--r--sound/isa/msnd/msnd_midi.c2
-rw-r--r--sound/isa/opti9xx/miro.c785
-rw-r--r--sound/isa/opti9xx/miro.h73
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c242
-rw-r--r--sound/isa/sb/emu8000.c6
-rw-r--r--sound/isa/sb/sb_mixer.c4
-rw-r--r--sound/isa/sscape.c727
-rw-r--r--sound/isa/wss/wss_lib.c105
-rw-r--r--sound/mips/sgio2audio.c2
-rw-r--r--sound/oss/Kconfig14
-rw-r--r--sound/oss/Makefile1
-rw-r--r--sound/oss/audio.c2
-rw-r--r--sound/oss/dmasound/dmasound_core.c4
-rw-r--r--sound/oss/dmasound/dmasound_paula.c2
-rw-r--r--sound/oss/hex2hex.c2
-rw-r--r--sound/oss/midi_synth.c2
-rw-r--r--sound/oss/mpu401.c2
-rw-r--r--sound/oss/pss.c6
-rw-r--r--sound/oss/sb_common.c4
-rw-r--r--sound/oss/sb_ess.c2
-rw-r--r--sound/oss/sh_dac_audio.c3
-rw-r--r--sound/oss/sscape.c1480
-rw-r--r--sound/pci/Kconfig1
-rw-r--r--sound/pci/ac97/ac97_codec.c8
-rw-r--r--sound/pci/ac97/ac97_patch.c12
-rw-r--r--sound/pci/azt3328.c4
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c4
-rw-r--r--sound/pci/ca0106/ca0106_proc.c6
-rw-r--r--sound/pci/cmipci.c4
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.asp9
-rw-r--r--sound/pci/cs5535audio/Makefile2
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c1
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h4
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c26
-rw-r--r--sound/pci/ctxfi/ctatc.c2
-rw-r--r--sound/pci/emu10k1/emu10k1x.c5
-rw-r--r--sound/pci/emu10k1/emumixer.c4
-rw-r--r--sound/pci/emu10k1/emuproc.c4
-rw-r--r--sound/pci/emu10k1/io.c2
-rw-r--r--sound/pci/es1938.c2
-rw-r--r--sound/pci/fm801.c40
-rw-r--r--sound/pci/hda/Kconfig13
-rw-r--r--sound/pci/hda/hda_beep.c114
-rw-r--r--sound/pci/hda/hda_beep.h10
-rw-r--r--sound/pci/hda/hda_codec.c617
-rw-r--r--sound/pci/hda/hda_codec.h16
-rw-r--r--sound/pci/hda/hda_eld.c20
-rw-r--r--sound/pci/hda/hda_generic.c17
-rw-r--r--sound/pci/hda/hda_hwdep.c45
-rw-r--r--sound/pci/hda/hda_intel.c74
-rw-r--r--sound/pci/hda/hda_local.h69
-rw-r--r--sound/pci/hda/hda_proc.c77
-rw-r--r--sound/pci/hda/patch_analog.c86
-rw-r--r--sound/pci/hda/patch_ca0110.c4
-rw-r--r--sound/pci/hda/patch_cirrus.c55
-rw-r--r--sound/pci/hda/patch_cmedia.c6
-rw-r--r--sound/pci/hda/patch_conexant.c248
-rw-r--r--sound/pci/hda/patch_intelhdmi.c584
-rw-r--r--sound/pci/hda/patch_nvhdmi.c2
-rw-r--r--sound/pci/hda/patch_realtek.c972
-rw-r--r--sound/pci/hda/patch_sigmatel.c290
-rw-r--r--sound/pci/hda/patch_via.c3509
-rw-r--r--sound/pci/ice1712/Makefile2
-rw-r--r--sound/pci/ice1712/aureon.c31
-rw-r--r--sound/pci/ice1712/ice1712.c12
-rw-r--r--sound/pci/ice1712/ice1712.h18
-rw-r--r--sound/pci/ice1712/ice1724.c103
-rw-r--r--sound/pci/ice1712/juli.c58
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c2
-rw-r--r--sound/pci/ice1712/quartet.c1130
-rw-r--r--sound/pci/ice1712/quartet.h10
-rw-r--r--sound/pci/intel8x0.c12
-rw-r--r--sound/pci/oxygen/Makefile3
-rw-r--r--sound/pci/oxygen/cs2000.h83
-rw-r--r--sound/pci/oxygen/hifier.c61
-rw-r--r--sound/pci/oxygen/oxygen.c248
-rw-r--r--sound/pci/oxygen/oxygen.h5
-rw-r--r--sound/pci/oxygen/oxygen_lib.c29
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c52
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c19
-rw-r--r--sound/pci/oxygen/virtuoso.c1105
-rw-r--r--sound/pci/oxygen/xonar.h50
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c434
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c128
-rw-r--r--sound/pci/oxygen/xonar_lib.c132
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c1115
-rw-r--r--sound/pci/rme9652/hdspm.c4
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c9
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c2
-rw-r--r--sound/pcmcia/vx/vxpocket.c6
-rw-r--r--sound/ppc/awacs.c12
-rw-r--r--sound/ppc/burgundy.c8
-rw-r--r--sound/ppc/tumbler.c2
-rw-r--r--sound/sh/Kconfig8
-rw-r--r--sound/sh/Makefile2
-rw-r--r--sound/sh/aica.c1
-rw-r--r--sound/sh/sh_dac_audio.c453
-rw-r--r--sound/soc/codecs/tlv320aic23.c2
-rw-r--r--sound/soc/codecs/twl4030.c10
-rw-r--r--sound/soc/codecs/uda134x.c4
-rw-r--r--sound/soc/codecs/wm8350.c25
-rw-r--r--sound/soc/codecs/wm8903.c6
-rw-r--r--sound/soc/codecs/wm8993.c4
-rw-r--r--sound/soc/codecs/wm9713.c22
-rw-r--r--sound/soc/omap/ams-delta.c4
-rw-r--r--sound/soc/omap/n810.c2
-rw-r--r--sound/soc/omap/omap-mcbsp.c6
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/omap/omap2evm.c2
-rw-r--r--sound/soc/omap/omap3beagle.c2
-rw-r--r--sound/soc/omap/omap3evm.c2
-rw-r--r--sound/soc/omap/osk5912.c2
-rw-r--r--sound/soc/omap/overo.c2
-rw-r--r--sound/soc/omap/sdp3430.c8
-rw-r--r--sound/soc/omap/zoom2.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.c4
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.h2
-rw-r--r--sound/soc/s6000/s6000-pcm.c2
-rw-r--r--sound/soc/soc-core.c2
-rw-r--r--sound/sound_core.c2
-rw-r--r--sound/synth/emux/soundfont.c2
-rw-r--r--sound/usb/usbaudio.c40
-rw-r--r--sound/usb/usbaudio.h9
-rw-r--r--sound/usb/usbmidi.c208
-rw-r--r--sound/usb/usbmixer.c9
-rw-r--r--sound/usb/usbmixer_maps.c23
-rw-r--r--sound/usb/usbquirks.h23
-rw-r--r--sound/usb/usx2y/us122l.c151
-rw-r--r--sound/usb/usx2y/us122l.h6
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c8
-rw-r--r--sound/usb/usx2y/usbusx2y.c28
-rw-r--r--sound/usb/usx2y/usbusx2y.h6
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c34
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c8
166 files changed, 12670 insertions, 5662 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index 439e15c8faa..fcad760f569 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,6 +1,3 @@
-# sound/Config.in
-#
-
menuconfig SOUND
tristate "Sound card support"
depends on HAS_IOMEM
@@ -58,7 +55,7 @@ config SOUND_OSS_CORE_PRECLAIM
Please read Documentation/feature-removal-schedule.txt for
details.
- If unusre, say Y.
+ If unsure, say Y.
source "sound/oss/dmasound/Kconfig"
@@ -136,4 +133,3 @@ config AC97_BUS
sound subsystem and other function drivers completely unrelated to
sound although they're sharing the AC97 bus. Concerned drivers
should "select" this.
-
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 5a549ed6c8a..8c0c851d464 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -3,7 +3,7 @@
#
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
-snd-aaci-objs := aaci.o devdma.o
+snd-aaci-objs := aaci.o
obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 1f0f8213e2d..c5699863643 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -18,10 +18,7 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/amba/bus.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/sizes.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -30,7 +27,6 @@
#include <sound/pcm_params.h>
#include "aaci.h"
-#include "devdma.h"
#define DRIVER_NAME "aaci-pl041"
@@ -176,14 +172,15 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
return v;
}
-static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
+static inline void
+aaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask)
{
u32 val;
int timeout = 5000;
do {
val = readl(aacirun->base + AACI_SR);
- } while (val & (SR_TXB|SR_RXB) && timeout--);
+ } while (val & mask && timeout--);
}
@@ -212,8 +209,10 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
writel(0, aacirun->base + AACI_IE);
return;
}
- ptr = aacirun->ptr;
+ spin_lock(&aacirun->lock);
+
+ ptr = aacirun->ptr;
do {
unsigned int len = aacirun->fifosz;
u32 val;
@@ -221,9 +220,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (aacirun->bytes <= 0) {
aacirun->bytes += aacirun->period;
aacirun->ptr = ptr;
- spin_unlock(&aaci->lock);
+ spin_unlock(&aacirun->lock);
snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aaci->lock);
+ spin_lock(&aacirun->lock);
}
if (!(aacirun->cr & CR_EN))
break;
@@ -249,7 +248,10 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
ptr = aacirun->start;
}
} while(1);
+
aacirun->ptr = ptr;
+
+ spin_unlock(&aacirun->lock);
}
if (mask & ISR_URINTR) {
@@ -267,6 +269,8 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
return;
}
+ spin_lock(&aacirun->lock);
+
ptr = aacirun->ptr;
do {
unsigned int len = aacirun->fifosz;
@@ -275,9 +279,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (aacirun->bytes <= 0) {
aacirun->bytes += aacirun->period;
aacirun->ptr = ptr;
- spin_unlock(&aaci->lock);
+ spin_unlock(&aacirun->lock);
snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aaci->lock);
+ spin_lock(&aacirun->lock);
}
if (!(aacirun->cr & CR_EN))
break;
@@ -305,6 +309,8 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
} while (1);
aacirun->ptr = ptr;
+
+ spin_unlock(&aacirun->lock);
}
}
@@ -314,7 +320,6 @@ static irqreturn_t aaci_irq(int irq, void *devid)
u32 mask;
int i;
- spin_lock(&aaci->lock);
mask = readl(aaci->base + AACI_ALLINTS);
if (mask) {
u32 m = mask;
@@ -324,7 +329,6 @@ static irqreturn_t aaci_irq(int irq, void *devid)
}
}
}
- spin_unlock(&aaci->lock);
return mask ? IRQ_HANDLED : IRQ_NONE;
}
@@ -334,63 +338,6 @@ static irqreturn_t aaci_irq(int irq, void *devid)
/*
* ALSA support.
*/
-
-struct aaci_stream {
- unsigned char codec_idx;
- unsigned char rate_idx;
-};
-
-static struct aaci_stream aaci_streams[] = {
- [ACSTREAM_FRONT] = {
- .codec_idx = 0,
- .rate_idx = AC97_RATES_FRONT_DAC,
- },
- [ACSTREAM_SURROUND] = {
- .codec_idx = 0,
- .rate_idx = AC97_RATES_SURR_DAC,
- },
- [ACSTREAM_LFE] = {
- .codec_idx = 0,
- .rate_idx = AC97_RATES_LFE_DAC,
- },
-};
-
-static inline unsigned int aaci_rate_mask(struct aaci *aaci, int streamid)
-{
- struct aaci_stream *s = aaci_streams + streamid;
- return aaci->ac97_bus->codec[s->codec_idx]->rates[s->rate_idx];
-}
-
-static unsigned int rate_list[] = {
- 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000
-};
-
-/*
- * Double-rate rule: we can support double rate iff channels == 2
- * (unimplemented)
- */
-static int
-aaci_rule_rate_by_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
-{
- struct aaci *aaci = rule->private;
- unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512;
- struct snd_interval *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
-
- switch (c->max) {
- case 6:
- rate_mask &= aaci_rate_mask(aaci, ACSTREAM_LFE);
- case 4:
- rate_mask &= aaci_rate_mask(aaci, ACSTREAM_SURROUND);
- case 2:
- rate_mask &= aaci_rate_mask(aaci, ACSTREAM_FRONT);
- }
-
- return snd_interval_list(hw_param_interval(p, rule->var),
- ARRAY_SIZE(rate_list), rate_list,
- rate_mask);
-}
-
static struct snd_pcm_hardware aaci_hw_info = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -404,10 +351,7 @@ static struct snd_pcm_hardware aaci_hw_info = {
*/
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- /* should this be continuous or knot? */
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
- .rate_max = 48000,
- .rate_min = 4000,
+ /* rates are setup from the AC'97 codec */
.channels_min = 2,
.channels_max = 6,
.buffer_bytes_max = 64 * 1024,
@@ -427,6 +371,12 @@ static int __aaci_pcm_open(struct aaci *aaci,
aacirun->substream = substream;
runtime->private_data = aacirun;
runtime->hw = aaci_hw_info;
+ runtime->hw.rates = aacirun->pcm->rates;
+ snd_pcm_limit_hw_rates(runtime);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ aacirun->pcm->r[1].slots)
+ snd_ac97_pcm_double_rate_rules(runtime);
/*
* FIXME: ALSA specifies fifo_size in bytes. If we're in normal
@@ -437,17 +387,6 @@ static int __aaci_pcm_open(struct aaci *aaci,
*/
runtime->hw.fifo_size = aaci->fifosize * 2;
- /*
- * Add rule describing hardware rate dependency
- * on the number of channels.
- */
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- aaci_rule_rate_by_channels, aaci,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- if (ret)
- goto out;
-
ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
DRIVER_NAME, aaci);
if (ret)
@@ -492,7 +431,7 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* Clear out the DMA and any allocated buffers.
*/
- devdma_hw_free(NULL, substream);
+ snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -504,27 +443,29 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
int err;
aaci_pcm_hw_free(substream);
+ if (aacirun->pcm_open) {
+ snd_ac97_pcm_close(aacirun->pcm);
+ aacirun->pcm_open = 0;
+ }
- err = devdma_hw_alloc(NULL, substream,
- params_buffer_bytes(params));
- if (err < 0)
- goto out;
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(params));
+ if (err >= 0) {
+ unsigned int rate = params_rate(params);
+ int dbl = rate > 48000;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
- params_channels(params),
- aacirun->pcm->r[0].slots);
- else
- err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+ err = snd_ac97_pcm_open(aacirun->pcm, rate,
params_channels(params),
- aacirun->pcm->r[1].slots);
+ aacirun->pcm->r[dbl].slots);
- if (err)
- goto out;
+ aacirun->pcm_open = err == 0;
+ aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+ aacirun->fifosz = aaci->fifosize * 4;
- aacirun->pcm_open = 1;
+ if (aacirun->cr & CR_COMPACT)
+ aacirun->fifosz >>= 1;
+ }
- out:
return err;
}
@@ -533,8 +474,8 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct aaci_runtime *aacirun = runtime->private_data;
- aacirun->start = (void *)runtime->dma_area;
- aacirun->end = aacirun->start + runtime->dma_bytes;
+ aacirun->start = runtime->dma_area;
+ aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
aacirun->ptr = aacirun->start;
aacirun->period =
aacirun->bytes = frames_to_bytes(runtime, runtime->period_size);
@@ -551,11 +492,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, bytes);
}
-static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- return devdma_mmap(NULL, substream, vma);
-}
-
/*
* Playback specific ALSA stuff
@@ -638,14 +574,9 @@ static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
* Enable FIFO, compact mode, 16 bits per sample.
* FIXME: double rate slots?
*/
- if (ret >= 0) {
- aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+ if (ret >= 0)
aacirun->cr |= channels_to_txmask[channels];
- aacirun->fifosz = aaci->fifosize * 4;
- if (aacirun->cr & CR_COMPACT)
- aacirun->fifosz >>= 1;
- }
return ret;
}
@@ -657,7 +588,7 @@ static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
ie &= ~(IE_URIE|IE_TXIE);
writel(ie, aacirun->base + AACI_IE);
aacirun->cr &= ~CR_EN;
- aaci_chan_wait_ready(aacirun);
+ aaci_chan_wait_ready(aacirun, SR_TXB);
writel(aacirun->cr, aacirun->base + AACI_TXCR);
}
@@ -665,7 +596,7 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
{
u32 ie;
- aaci_chan_wait_ready(aacirun);
+ aaci_chan_wait_ready(aacirun, SR_TXB);
aacirun->cr |= CR_EN;
ie = readl(aacirun->base + AACI_IE);
@@ -676,12 +607,12 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct aaci *aaci = substream->private_data;
struct aaci_runtime *aacirun = substream->runtime->private_data;
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(&aaci->lock, flags);
+ spin_lock_irqsave(&aacirun->lock, flags);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aaci_pcm_playback_start(aacirun);
@@ -708,7 +639,8 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm
default:
ret = -EINVAL;
}
- spin_unlock_irqrestore(&aaci->lock, flags);
+
+ spin_unlock_irqrestore(&aacirun->lock, flags);
return ret;
}
@@ -722,7 +654,6 @@ static struct snd_pcm_ops aaci_playback_ops = {
.prepare = aaci_pcm_prepare,
.trigger = aaci_pcm_playback_trigger,
.pointer = aaci_pcm_pointer,
- .mmap = aaci_pcm_mmap,
};
static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
@@ -733,18 +664,10 @@ static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
int ret;
ret = aaci_pcm_hw_params(substream, aacirun, params);
-
- if (ret >= 0) {
- aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
-
+ if (ret >= 0)
/* Line in record: slot 3 and 4 */
aacirun->cr |= CR_SL3 | CR_SL4;
- aacirun->fifosz = aaci->fifosize * 4;
-
- if (aacirun->cr & CR_COMPACT)
- aacirun->fifosz >>= 1;
- }
return ret;
}
@@ -752,7 +675,7 @@ static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
{
u32 ie;
- aaci_chan_wait_ready(aacirun);
+ aaci_chan_wait_ready(aacirun, SR_RXB);
ie = readl(aacirun->base + AACI_IE);
ie &= ~(IE_ORIE | IE_RXIE);
@@ -767,7 +690,7 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
{
u32 ie;
- aaci_chan_wait_ready(aacirun);
+ aaci_chan_wait_ready(aacirun, SR_RXB);
#ifdef DEBUG
/* RX Timeout value: bits 28:17 in RXCR */
@@ -784,12 +707,11 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct aaci *aaci = substream->private_data;
struct aaci_runtime *aacirun = substream->runtime->private_data;
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(&aaci->lock, flags);
+ spin_lock_irqsave(&aacirun->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -818,7 +740,7 @@ static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd
ret = -EINVAL;
}
- spin_unlock_irqrestore(&aaci->lock, flags);
+ spin_unlock_irqrestore(&aacirun->lock, flags);
return ret;
}
@@ -850,7 +772,6 @@ static struct snd_pcm_ops aaci_capture_ops = {
.prepare = aaci_pcm_capture_prepare,
.trigger = aaci_pcm_capture_trigger,
.pointer = aaci_pcm_pointer,
- .mmap = aaci_pcm_mmap,
};
/*
@@ -902,6 +823,12 @@ static struct ac97_pcm ac97_defs[] __devinitdata = {
(1 << AC97_SLOT_PCM_SRIGHT) |
(1 << AC97_SLOT_LFE),
},
+ [1] = {
+ .slots = (1 << AC97_SLOT_PCM_LEFT) |
+ (1 << AC97_SLOT_PCM_RIGHT) |
+ (1 << AC97_SLOT_PCM_LEFT_0) |
+ (1 << AC97_SLOT_PCM_RIGHT_0),
+ },
},
},
[1] = { /* PCM in */
@@ -1014,7 +941,6 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
aaci = card->private_data;
mutex_init(&aaci->ac97_sem);
- spin_lock_init(&aaci->lock);
aaci->card = card;
aaci->dev = dev;
@@ -1040,6 +966,8 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 1024);
}
return ret;
@@ -1099,12 +1027,14 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
/*
* Playback uses AACI channel 0
*/
+ spin_lock_init(&aaci->playback.lock);
aaci->playback.base = aaci->base + AACI_CSCH1;
aaci->playback.fifo = aaci->base + AACI_DR1;
/*
* Capture uses AACI channel 0
*/
+ spin_lock_init(&aaci->capture.lock);
aaci->capture.base = aaci->base + AACI_CSCH1;
aaci->capture.fifo = aaci->base + AACI_DR1;
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
index 924f69c1c44..6a4a2eebdda 100644
--- a/sound/arm/aaci.h
+++ b/sound/arm/aaci.h
@@ -202,6 +202,7 @@
struct aaci_runtime {
void __iomem *base;
void __iomem *fifo;
+ spinlock_t lock;
struct ac97_pcm *pcm;
int pcm_open;
@@ -232,7 +233,6 @@ struct aaci {
struct snd_ac97 *ac97;
u32 maincr;
- spinlock_t lock;
struct aaci_runtime playback;
struct aaci_runtime capture;
diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
deleted file mode 100644
index 9d1e6665b54..00000000000
--- a/sound/arm/devdma.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * linux/sound/arm/devdma.c
- *
- * Copyright (C) 2003-2004 Russell King, All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * ARM DMA shim for ALSA.
- */
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-
-#include "devdma.h"
-
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
- if (runtime->dma_area == NULL)
- return;
-
- if (buf != &substream->dma_buffer) {
- dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
- kfree(runtime->dma_buffer_p);
- }
-
- snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
- int ret = 0;
-
- if (buf) {
- if (buf->bytes >= size)
- goto out;
- devdma_hw_free(dev, substream);
- }
-
- if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
- buf = &substream->dma_buffer;
- } else {
- buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
- if (!buf)
- goto nomem;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = dev;
- buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL);
- buf->bytes = size;
- buf->private_data = NULL;
-
- if (!buf->area)
- goto free;
- }
- snd_pcm_set_runtime_buffer(substream, buf);
- ret = 1;
- out:
- runtime->dma_bytes = size;
- return ret;
-
- free:
- kfree(buf);
- nomem:
- return -ENOMEM;
-}
-
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-}
diff --git a/sound/arm/devdma.h b/sound/arm/devdma.h
deleted file mode 100644
index d025329c8a0..00000000000
--- a/sound/arm/devdma.h
+++ /dev/null
@@ -1,3 +0,0 @@
-void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream);
-int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size);
-int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma);
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index b4b48afb6de..5d9411839cd 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -159,7 +159,7 @@ static int pxa2xx_ac97_resume(struct device *dev)
return ret;
}
-static struct dev_pm_ops pxa2xx_ac97_pm_ops = {
+static const struct dev_pm_ops pxa2xx_ac97_pm_ops = {
.suspend = pxa2xx_ac97_suspend,
.resume = pxa2xx_ac97_resume,
};
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index c15682a2f9d..475455c7661 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -5,6 +5,7 @@ config SND_TIMER
config SND_PCM
tristate
select SND_TIMER
+ select GCD
config SND_HWDEP
tristate
diff --git a/sound/core/control.c b/sound/core/control.c
index a8b7fabe645..268ab747122 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -75,7 +75,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
- ctl->pid = current->pid;
+ ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
list_add_tail(&ctl->list, &card->ctl_files);
@@ -125,6 +125,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
control->vd[idx].owner = NULL;
up_write(&card->controls_rwsem);
snd_ctl_empty_read_queue(ctl);
+ put_pid(ctl->pid);
kfree(ctl);
module_put(card->module);
snd_card_file_remove(card, file);
@@ -672,7 +673,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
if (vd->owner == ctl)
info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
- info->owner = vd->owner_pid;
+ info->owner = pid_vnr(vd->owner->pid);
} else {
info->owner = -1;
}
@@ -827,7 +828,6 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
result = -EBUSY;
else {
vd->owner = file;
- vd->owner_pid = current->pid;
result = 0;
}
}
@@ -858,7 +858,6 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
result = -EPERM;
else {
vd->owner = NULL;
- vd->owner_pid = 0;
result = 0;
}
}
@@ -1120,7 +1119,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
goto __kctl_end;
}
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- if (file && vd->owner != NULL && vd->owner != file) {
+ if (vd->owner != NULL && vd->owner != file) {
err = -EPERM;
goto __kctl_end;
}
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 34c7d48f506..7f4d744ae40 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -37,14 +37,22 @@ static unsigned int resolution;
struct snd_hrtimer {
struct snd_timer *timer;
struct hrtimer hrt;
+ atomic_t running;
};
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
{
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
struct snd_timer *t = stime->timer;
+
+ if (!atomic_read(&stime->running))
+ return HRTIMER_NORESTART;
+
hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
snd_timer_interrupt(stime->timer, t->sticks);
+
+ if (!atomic_read(&stime->running))
+ return HRTIMER_NORESTART;
return HRTIMER_RESTART;
}
@@ -58,6 +66,7 @@ static int snd_hrtimer_open(struct snd_timer *t)
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
stime->timer = t;
stime->hrt.function = snd_hrtimer_callback;
+ atomic_set(&stime->running, 0);
t->private_data = stime;
return 0;
}
@@ -78,16 +87,18 @@ static int snd_hrtimer_start(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
+ atomic_set(&stime->running, 0);
+ hrtimer_cancel(&stime->hrt);
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL);
+ atomic_set(&stime->running, 1);
return 0;
}
static int snd_hrtimer_stop(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
-
- hrtimer_cancel(&stime->hrt);
+ atomic_set(&stime->running, 0);
return 0;
}
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 79f0f16af33..950e19ba91f 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -85,16 +85,24 @@ EXPORT_SYMBOL(snd_dma_disable);
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
unsigned long flags;
- unsigned int result;
+ unsigned int result, result1;
flags = claim_dma_lock();
clear_dma_ff(dma);
if (!isa_dma_bridge_buggy)
disable_dma(dma);
result = get_dma_residue(dma);
+ /*
+ * HACK - read the counter again and choose higher value in order to
+ * avoid reading during counter lower byte roll over if the
+ * isa_dma_bridge_buggy is set.
+ */
+ result1 = get_dma_residue(dma);
if (!isa_dma_bridge_buggy)
enable_dma(dma);
release_dma_lock(flags);
+ if (unlikely(result < result1))
+ result = result1;
#ifdef CONFIG_SND_DEBUG
if (result > size)
snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 772423889eb..54e2eb56e4c 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1251,7 +1251,9 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{ SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
{ SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
{ SOUND_MIXER_PCM, "PCM", 0 },
- { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
+ { SOUND_MIXER_SPEAKER, "Beep", 0 },
+ { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, /* fallback */
+ { SOUND_MIXER_SPEAKER, "Speaker", 0 }, /* fallback */
{ SOUND_MIXER_LINE, "Line", 0 },
{ SOUND_MIXER_MIC, "Mic", 0 },
{ SOUND_MIXER_CD, "CD", 0 },
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index c69c60b2a48..6884ae031f6 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -435,6 +435,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
return;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+ snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
@@ -809,7 +810,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
card = pcm->card;
read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == current->pid) {
+ if (kctl->pid == task_pid(current)) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
break;
@@ -900,6 +901,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
substream->private_data = pcm->private_data;
substream->ref_count = 1;
substream->f_flags = file->f_flags;
+ substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
return 0;
@@ -921,6 +923,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
kfree(runtime->hw_constraints.rules);
kfree(runtime);
substream->runtime = NULL;
+ put_pid(substream->pid);
+ substream->pid = NULL;
substream->pstr->substream_opened--;
}
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 30f410832a2..a27545b23ee 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -758,7 +758,7 @@ int snd_interval_ratnum(struct snd_interval *i,
int diff;
if (q == 0)
q = 1;
- den = div_down(num, q);
+ den = div_up(num, q);
if (den < rats[k].den_min)
continue;
if (den > rats[k].den_max)
@@ -794,7 +794,7 @@ int snd_interval_ratnum(struct snd_interval *i,
i->empty = 1;
return -EINVAL;
}
- den = div_up(num, q);
+ den = div_down(num, q);
if (den > rats[k].den_max)
continue;
if (den < rats[k].den_min)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index ab73edf2c89..29ab46a12e1 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -26,6 +26,7 @@
#include <linux/time.h>
#include <linux/pm_qos_params.h>
#include <linux/uio.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -3061,6 +3062,27 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
+static inline struct page *
+snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
+{
+ void *vaddr = substream->runtime->dma_area + ofs;
+#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return virt_to_page(CAC_ADDR(vaddr));
+#endif
+#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
+ if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
+ dma_addr_t addr = substream->runtime->dma_addr + ofs;
+ addr -= get_dma_offset(substream->dma_buffer.dev.dev);
+ /* assume dma_handle set via pfn_to_phys() in
+ * mm/dma-noncoherent.c
+ */
+ return pfn_to_page(addr >> PAGE_SHIFT);
+ }
+#endif
+ return virt_to_page(vaddr);
+}
+
/*
* fault callback for mmapping a RAM page
*/
@@ -3071,7 +3093,6 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
struct snd_pcm_runtime *runtime;
unsigned long offset;
struct page * page;
- void *vaddr;
size_t dma_bytes;
if (substream == NULL)
@@ -3081,36 +3102,53 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
if (offset > dma_bytes - PAGE_SIZE)
return VM_FAULT_SIGBUS;
- if (substream->ops->page) {
+ if (substream->ops->page)
page = substream->ops->page(substream, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
- } else {
- vaddr = runtime->dma_area + offset;
- page = virt_to_page(vaddr);
- }
+ else
+ page = snd_pcm_default_page_ops(substream, offset);
+ if (!page)
+ return VM_FAULT_SIGBUS;
get_page(page);
vmf->page = page;
return 0;
}
-static const struct vm_operations_struct snd_pcm_vm_ops_data =
-{
+static const struct vm_operations_struct snd_pcm_vm_ops_data = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.open = snd_pcm_mmap_data_open,
.close = snd_pcm_mmap_data_close,
.fault = snd_pcm_mmap_data_fault,
};
+#ifndef ARCH_HAS_DMA_MMAP_COHERENT
+/* This should be defined / handled globally! */
+#ifdef CONFIG_ARM
+#define ARCH_HAS_DMA_MMAP_COHERENT
+#endif
+#endif
+
/*
* mmap the DMA buffer on RAM
*/
static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- area->vm_ops = &snd_pcm_vm_ops_data;
- area->vm_private_data = substream;
area->vm_flags |= VM_RESERVED;
- atomic_inc(&substream->mmap_count);
+#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+ if (!substream->ops->page &&
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return dma_mmap_coherent(substream->dma_buffer.dev.dev,
+ area,
+ substream->runtime->dma_area,
+ substream->runtime->dma_addr,
+ area->vm_end - area->vm_start);
+#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+ /* mmap with fault handler */
+ area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
}
@@ -3118,12 +3156,6 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
-static const struct vm_operations_struct snd_pcm_vm_ops_data_mmio =
-{
- .open = snd_pcm_mmap_data_open,
- .close = snd_pcm_mmap_data_close,
-};
-
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@@ -3133,8 +3165,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
#ifdef pgprot_noncached
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
#endif
- area->vm_ops = &snd_pcm_vm_ops_data_mmio;
- area->vm_private_data = substream;
area->vm_flags |= VM_IO;
size = area->vm_end - area->vm_start;
offset = area->vm_pgoff << PAGE_SHIFT;
@@ -3142,7 +3172,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
size, area->vm_page_prot))
return -EAGAIN;
- atomic_inc(&substream->mmap_count);
return 0;
}
@@ -3159,6 +3188,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
long size;
unsigned long offset;
size_t dma_bytes;
+ int err;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!(area->vm_flags & (VM_WRITE|VM_READ)))
@@ -3183,10 +3213,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
if (offset > dma_bytes - size)
return -EINVAL;
+ area->vm_ops = &snd_pcm_vm_ops_data;
+ area->vm_private_data = substream;
if (substream->ops->mmap)
- return substream->ops->mmap(substream, area);
+ err = substream->ops->mmap(substream, area);
else
- return snd_pcm_default_mmap(substream, area);
+ err = snd_pcm_default_mmap(substream, area);
+ if (!err)
+ atomic_inc(&substream->mmap_count);
+ return err;
}
EXPORT_SYMBOL(snd_pcm_mmap_data);
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index ca8068b63d6..b01d9481d63 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -20,6 +20,7 @@
*/
#include <linux/time.h>
+#include <linux/gcd.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/timer.h>
@@ -28,22 +29,6 @@
* Timer functions
*/
-/* Greatest common divisor */
-static unsigned long gcd(unsigned long a, unsigned long b)
-{
- unsigned long r;
- if (a < b) {
- r = a;
- a = b;
- b = r;
- }
- while ((r = a % b) != 0) {
- a = b;
- b = r;
- }
- return b;
-}
-
void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
{
unsigned long rate, mult, fsize, l, post;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index c0adc14c91f..0f5a194695d 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -242,13 +242,12 @@ static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
return -ENXIO;
if (subdevice >= 0 && subdevice >= s->substream_count)
return -ENODEV;
- if (s->substream_opened >= s->substream_count)
- return -EAGAIN;
list_for_each_entry(substream, &s->substreams, list) {
if (substream->opened) {
if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
- !(mode & SNDRV_RAWMIDI_LFLG_APPEND))
+ !(mode & SNDRV_RAWMIDI_LFLG_APPEND) ||
+ !substream->append)
continue;
}
if (subdevice < 0 || subdevice == substream->number) {
@@ -266,18 +265,23 @@ static int open_substream(struct snd_rawmidi *rmidi,
{
int err;
- err = snd_rawmidi_runtime_create(substream);
- if (err < 0)
- return err;
- err = substream->ops->open(substream);
- if (err < 0)
- return err;
- substream->opened = 1;
- if (substream->use_count++ == 0)
+ if (substream->use_count == 0) {
+ err = snd_rawmidi_runtime_create(substream);
+ if (err < 0)
+ return err;
+ err = substream->ops->open(substream);
+ if (err < 0) {
+ snd_rawmidi_runtime_free(substream);
+ return err;
+ }
+ substream->opened = 1;
substream->active_sensing = 0;
- if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
- substream->append = 1;
- rmidi->streams[substream->stream].substream_opened++;
+ if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+ substream->append = 1;
+ substream->pid = get_pid(task_pid(current));
+ rmidi->streams[substream->stream].substream_opened++;
+ }
+ substream->use_count++;
return 0;
}
@@ -297,27 +301,27 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
SNDRV_RAWMIDI_STREAM_INPUT,
mode, &sinput);
if (err < 0)
- goto __error;
+ return err;
}
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
err = assign_substream(rmidi, subdevice,
SNDRV_RAWMIDI_STREAM_OUTPUT,
mode, &soutput);
if (err < 0)
- goto __error;
+ return err;
}
if (sinput) {
err = open_substream(rmidi, sinput, mode);
if (err < 0)
- goto __error;
+ return err;
}
if (soutput) {
err = open_substream(rmidi, soutput, mode);
if (err < 0) {
if (sinput)
close_substream(rmidi, sinput, 0);
- goto __error;
+ return err;
}
}
@@ -325,13 +329,6 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
rfile->input = sinput;
rfile->output = soutput;
return 0;
-
- __error:
- if (sinput && sinput->runtime)
- snd_rawmidi_runtime_free(sinput);
- if (soutput && soutput->runtime)
- snd_rawmidi_runtime_free(soutput);
- return err;
}
/* called from sound/core/seq/seq_midi.c */
@@ -415,7 +412,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
subdevice = -1;
read_lock(&card->ctl_files_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == current->pid) {
+ if (kctl->pid == task_pid(current)) {
subdevice = kctl->prefer_rawmidi_subdevice;
if (subdevice != -1)
break;
@@ -468,7 +465,6 @@ static void close_substream(struct snd_rawmidi *rmidi,
struct snd_rawmidi_substream *substream,
int cleanup)
{
- rmidi->streams[substream->stream].substream_opened--;
if (--substream->use_count)
return;
@@ -493,6 +489,9 @@ static void close_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
substream->opened = 0;
substream->append = 0;
+ put_pid(substream->pid);
+ substream->pid = NULL;
+ rmidi->streams[substream->stream].substream_opened--;
}
static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
@@ -1258,7 +1257,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
break;
count -= count1;
}
- if (file->f_flags & O_SYNC) {
+ if (file->f_flags & O_DSYNC) {
spin_lock_irq(&runtime->lock);
while (runtime->avail != runtime->buffer_size) {
wait_queue_t wait;
@@ -1340,6 +1339,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Mode : %s\n"
@@ -1361,6 +1363,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
substream->number,
(unsigned long) substream->bytes);
if (substream->opened) {
+ snd_iprintf(buffer,
+ " Owner PID : %d\n",
+ pid_vnr(substream->pid));
runtime = substream->runtime;
snd_iprintf(buffer,
" Buffer size : %lu\n"
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index b60cef257b5..f165c77d627 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -26,6 +26,7 @@ MODULE_ALIAS("platform:pcspkr");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+static int nopcm; /* Disable PCM capability of the driver */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
@@ -33,6 +34,8 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
+module_param(nopcm, bool, 0444);
+MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
struct snd_pcsp pcsp_chip;
@@ -43,13 +46,16 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
int err;
int div, min_div, order;
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
- printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%linS)\n", tp.tv_nsec);
- printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
- "enabled.\n");
- return -EIO;
+ if (!nopcm) {
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+ printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
+ "(%linS)\n", tp.tv_nsec);
+ printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
+ "enabled.\n");
+ printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
+ nopcm = 1;
+ }
}
if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
@@ -107,12 +113,14 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
snd_card_free(card);
return err;
}
- err = snd_pcsp_new_pcm(&pcsp_chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
+ if (!nopcm) {
+ err = snd_pcsp_new_pcm(&pcsp_chip);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
}
- err = snd_pcsp_new_mixer(&pcsp_chip);
+ err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
if (err < 0) {
snd_card_free(card);
return err;
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 174dd2ff0f2..1e123077923 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -83,6 +83,6 @@ extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
extern void pcsp_sync_stop(struct snd_pcsp *chip);
extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
-extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
+extern int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm);
#endif
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c
index 903bc846763..6f633f4f3b9 100644
--- a/sound/drivers/pcsp/pcsp_mixer.c
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -119,24 +119,43 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
.put = pcsp_##ctl_type##_put, \
}
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = {
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_pcm[] = {
PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
- PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"),
};
-int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip)
+static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_spkr[] = {
+ PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
+};
+
+static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
+ struct snd_kcontrol_new *ctls, int num)
{
- struct snd_card *card = chip->card;
int i, err;
+ struct snd_card *card = chip->card;
+ for (i = 0; i < num; i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(ctls + i, chip));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
+{
+ int err;
+ struct snd_card *card = chip->card;
- for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(snd_pcsp_controls + i,
- chip));
+ if (!nopcm) {
+ err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_pcm,
+ ARRAY_SIZE(snd_pcsp_controls_pcm));
if (err < 0)
return err;
}
+ err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_spkr,
+ ARRAY_SIZE(snd_pcsp_controls_spkr));
+ if (err < 0)
+ return err;
strcpy(card->mixername, "PC-Speaker");
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 020a5d51247..04ae8704cdc 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/bitrev.h>
#include <asm/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -55,18 +56,6 @@ struct cs8427 {
struct cs8427_stream capture;
};
-static unsigned char swapbits(unsigned char val)
-{
- int bit;
- unsigned char res = 0;
- for (bit = 0; bit < 8; bit++) {
- res <<= 1;
- res |= val & 1;
- val >>= 1;
- }
- return res;
-}
-
int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
unsigned char val)
{
@@ -149,7 +138,7 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
}
data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
for (idx = 0; idx < count; idx++)
- data[idx + 1] = swapbits(ndata[idx]);
+ data[idx + 1] = bitrev8(ndata[idx]);
if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)
return -EIO;
return 1;
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 703d954238f..2dad40f3f62 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -5,6 +5,7 @@
snd-ak4114-objs := ak4114.o
snd-ak4117-objs := ak4117.o
+snd-ak4113-objs := ak4113.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o
snd-pt2258-objs := pt2258.o
snd-tea575x-tuner-objs := tea575x-tuner.o
@@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
new file mode 100644
index 00000000000..fff62cc8607
--- /dev/null
+++ b/sound/i2c/other/ak4113.c
@@ -0,0 +1,639 @@
+/*
+ * Routines for control of the AK4113 via I2C/4-wire serial interface
+ * IEC958 (S/PDIF) receiver by Asahi Kasei
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4113.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
+MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4113_ADDR 0x00 /* fixed address */
+
+static void ak4113_stats(struct work_struct *work);
+static void ak4113_init_regs(struct ak4113 *chip);
+
+
+static void reg_write(struct ak4113 *ak4113, unsigned char reg,
+ unsigned char val)
+{
+ ak4113->write(ak4113->private_data, reg, val);
+ if (reg < sizeof(ak4113->regmap))
+ ak4113->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
+{
+ return ak4113->read(ak4113->private_data, reg);
+}
+
+static void snd_ak4113_free(struct ak4113 *chip)
+{
+ chip->init = 1; /* don't schedule new work */
+ mb();
+ cancel_delayed_work(&chip->work);
+ flush_scheduled_work();
+ kfree(chip);
+}
+
+static int snd_ak4113_dev_free(struct snd_device *device)
+{
+ struct ak4113 *chip = device->device_data;
+ snd_ak4113_free(chip);
+ return 0;
+}
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+ ak4113_write_t *write, const unsigned char pgm[5],
+ void *private_data, struct ak4113 **r_ak4113)
+{
+ struct ak4113 *chip;
+ int err = 0;
+ unsigned char reg;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_ak4113_dev_free,
+ };
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->read = read;
+ chip->write = write;
+ chip->private_data = private_data;
+ INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+
+ for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
+ chip->regmap[reg] = pgm[reg];
+ ak4113_init_regs(chip);
+
+ chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
+ AK4113_CINT | AK4113_STC);
+ chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
+ chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0)
+ goto __fail;
+
+ if (r_ak4113)
+ *r_ak4113 = chip;
+ return 0;
+
+__fail:
+ snd_ak4113_free(chip);
+ return err < 0 ? err : -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_create);
+
+void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
+ unsigned char mask, unsigned char val)
+{
+ if (reg >= AK4113_WRITABLE_REGS)
+ return;
+ reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
+
+static void ak4113_init_regs(struct ak4113 *chip)
+{
+ unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
+
+ /* bring the chip to reset state and powerdown state */
+ reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
+ udelay(200);
+ /* release reset, but leave powerdown */
+ reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
+ udelay(200);
+ for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
+ reg_write(chip, reg, chip->regmap[reg]);
+ /* release powerdown, everything is initialized now */
+ reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
+}
+
+void snd_ak4113_reinit(struct ak4113 *chip)
+{
+ chip->init = 1;
+ mb();
+ flush_scheduled_work();
+ ak4113_init_regs(chip);
+ /* bring up statistics / event queing */
+ chip->init = 0;
+ if (chip->kctls[0])
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+ switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
+ case AK4113_FS_8000HZ:
+ return 8000;
+ case AK4113_FS_11025HZ:
+ return 11025;
+ case AK4113_FS_16000HZ:
+ return 16000;
+ case AK4113_FS_22050HZ:
+ return 22050;
+ case AK4113_FS_24000HZ:
+ return 24000;
+ case AK4113_FS_32000HZ:
+ return 32000;
+ case AK4113_FS_44100HZ:
+ return 44100;
+ case AK4113_FS_48000HZ:
+ return 48000;
+ case AK4113_FS_64000HZ:
+ return 64000;
+ case AK4113_FS_88200HZ:
+ return 88200;
+ case AK4113_FS_96000HZ:
+ return 96000;
+ case AK4113_FS_176400HZ:
+ return 176400;
+ case AK4113_FS_192000HZ:
+ return 192000;
+ default:
+ return 0;
+ }
+}
+
+static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = LONG_MAX;
+ return 0;
+}
+
+static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ long *ptr;
+
+ spin_lock_irq(&chip->lock);
+ ptr = (long *)(((char *)chip) + kcontrol->private_value);
+ ucontrol->value.integer.value[0] = *ptr;
+ *ptr = 0;
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+#define snd_ak4113_in_bit_info snd_ctl_boolean_mono_info
+
+static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char reg = kcontrol->private_value & 0xff;
+ unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+ unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+ ucontrol->value.integer.value[0] =
+ ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+ return 0;
+}
+
+static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 5;
+ return 0;
+}
+
+static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ (AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
+ return 0;
+}
+
+static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ int change;
+ u8 old_val;
+
+ spin_lock_irq(&chip->lock);
+ old_val = chip->regmap[AK4113_REG_IO1];
+ change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
+ if (change)
+ reg_write(chip, AK4113_REG_IO1,
+ (old_val & (~AK4113_IPS(0xff))) |
+ (AK4113_IPS(ucontrol->value.integer.value[0])));
+ spin_unlock_irq(&chip->lock);
+ return change;
+}
+
+static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+ return 0;
+}
+
+static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
+ AK4113_REG_RCS1));
+ return 0;
+}
+
+static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
+ ucontrol->value.iec958.status[i] = reg_read(chip,
+ AK4113_REG_RXCSB0 + i);
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
+ return 0;
+}
+
+static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ uinfo->count = 4;
+ return 0;
+}
+
+static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short tmp;
+
+ ucontrol->value.integer.value[0] = 0xf8f2;
+ ucontrol->value.integer.value[1] = 0x4e1f;
+ tmp = reg_read(chip, AK4113_REG_Pc0) |
+ (reg_read(chip, AK4113_REG_Pc1) << 8);
+ ucontrol->value.integer.value[2] = tmp;
+ tmp = reg_read(chip, AK4113_REG_Pd0) |
+ (reg_read(chip, AK4113_REG_Pd1) << 8);
+ ucontrol->value.integer.value[3] = tmp;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = AK4113_REG_QSUB_SIZE;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
+ ucontrol->value.bytes.data[i] = reg_read(chip,
+ AK4113_REG_QSUB_ADDR + i);
+ return 0;
+}
+
+/* Don't forget to change AK4113_CONTROLS define!!! */
+static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Parity Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, parity_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, v_bit_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 C-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, ccrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, qcrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 External Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_rate_info,
+ .get = snd_ak4113_rate_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ak4113_spdif_mask_info,
+ .get = snd_ak4113_spdif_mask_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_info,
+ .get = snd_ak4113_spdif_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Preample Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_pinfo,
+ .get = snd_ak4113_spdif_pget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_qinfo,
+ .get = snd_ak4113_spdif_qget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Audio",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Non-PCM Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (0<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 DTS Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "AK4113 Input Select",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ak4113_rx_info,
+ .get = snd_ak4113_rx_get,
+ .put = snd_ak4113_rx_put,
+}
+};
+
+static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct ak4113 *ak4113 = entry->private_data;
+ int reg, val;
+ /* all ak4113 registers 0x00 - 0x1c */
+ for (reg = 0; reg < 0x1d; reg++) {
+ val = reg_read(ak4113, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void snd_ak4113_proc_init(struct ak4113 *ak4113)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
+ snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+}
+
+int snd_ak4113_build(struct ak4113 *ak4113,
+ struct snd_pcm_substream *cap_substream)
+{
+ struct snd_kcontrol *kctl;
+ unsigned int idx;
+ int err;
+
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
+ ak4113->substream = cap_substream;
+ for (idx = 0; idx < AK4113_CONTROLS; idx++) {
+ kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
+ if (kctl == NULL)
+ return -ENOMEM;
+ kctl->id.device = cap_substream->pcm->device;
+ kctl->id.subdevice = cap_substream->number;
+ err = snd_ctl_add(ak4113->card, kctl);
+ if (err < 0)
+ return err;
+ ak4113->kctls[idx] = kctl;
+ }
+ snd_ak4113_proc_init(ak4113);
+ /* trigger workq */
+ schedule_delayed_work(&ak4113->work, HZ / 10);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_build);
+
+int snd_ak4113_external_rate(struct ak4113 *ak4113)
+{
+ unsigned char rcs1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ return external_rate(rcs1);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
+
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
+{
+ struct snd_pcm_runtime *runtime =
+ ak4113->substream ? ak4113->substream->runtime : NULL;
+ unsigned long _flags;
+ int res = 0;
+ unsigned char rcs0, rcs1, rcs2;
+ unsigned char c0, c1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ if (flags & AK4113_CHECK_NO_STAT)
+ goto __rate;
+ rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
+ rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
+ spin_lock_irqsave(&ak4113->lock, _flags);
+ if (rcs0 & AK4113_PAR)
+ ak4113->parity_errors++;
+ if (rcs0 & AK4113_V)
+ ak4113->v_bit_errors++;
+ if (rcs2 & AK4113_CCRC)
+ ak4113->ccrc_errors++;
+ if (rcs2 & AK4113_QCRC)
+ ak4113->qcrc_errors++;
+ c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+ (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+ c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0)) ^
+ (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0));
+ ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+ ak4113->rcs1 = rcs1;
+ ak4113->rcs2 = rcs2;
+ spin_unlock_irqrestore(&ak4113->lock, _flags);
+
+ if (rcs0 & AK4113_PAR)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[0]->id);
+ if (rcs0 & AK4113_V)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[1]->id);
+ if (rcs2 & AK4113_CCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[2]->id);
+ if (rcs2 & AK4113_QCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[3]->id);
+
+ /* rate change */
+ if (c1 & 0xf0)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[4]->id);
+
+ if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[6]->id);
+ if (c0 & AK4113_QINT)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[8]->id);
+
+ if (c0 & AK4113_AUDION)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[9]->id);
+ if (c1 & AK4113_NPCM)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[10]->id);
+ if (c1 & AK4113_DTSCD)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[11]->id);
+
+ if (ak4113->change_callback && (c0 | c1) != 0)
+ ak4113->change_callback(ak4113, c0, c1);
+
+__rate:
+ /* compare rate */
+ res = external_rate(rcs1);
+ if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
+ (runtime->rate != res)) {
+ snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
+ if (snd_pcm_running(ak4113->substream)) {
+ /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
+ * runtime->rate, res); */
+ snd_pcm_stop(ak4113->substream,
+ SNDRV_PCM_STATE_DRAINING);
+ wake_up(&runtime->sleep);
+ res = 1;
+ }
+ snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
+
+static void ak4113_stats(struct work_struct *work)
+{
+ struct ak4113 *chip = container_of(work, struct ak4113, work.work);
+
+ if (!chip->init)
+ snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
+
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index ee47abab764..1adb8a3c2b6 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
#include <asm/io.h>
#include <linux/delay.h>
@@ -29,6 +29,7 @@
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
+#include <sound/info.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
@@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
static void ak4524_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
- unsigned char reg, maxreg;
+ unsigned char reg;
- if (ak->type == SND_AK4528)
- maxreg = 0x06;
- else
- maxreg = 0x08;
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
if (state)
continue;
/* DAC volumes */
- for (reg = 0x04; reg < maxreg; reg++)
+ for (reg = 0x04; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
}
/* reset procedure for AK4355 and AK4358 */
-static void ak435X_reset(struct snd_akm4xxx *ak, int state,
- unsigned char total_regs)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state)
{
unsigned char reg;
@@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state,
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return;
}
- for (reg = 0x00; reg < total_regs; reg++)
+ for (reg = 0x00; reg < ak->total_regs; reg++)
if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(ak, 0, reg));
@@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
unsigned char reg;
-
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
if (state)
continue;
- for (reg = 0x01; reg < 0x05; reg++)
+ for (reg = 0x01; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
@@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
ak4524_reset(ak, state);
break;
case SND_AK4529:
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
- ak435X_reset(ak, state, 0x0b);
+ ak435X_reset(ak, state);
break;
case SND_AK4358:
- ak435X_reset(ak, state, 0x10);
+ ak435X_reset(ak, state);
break;
case SND_AK4381:
ak4381_reset(ak, state);
@@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
* Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB
*
- * Used for AK4524 input/ouput attenuation, AK4528, and
+ * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
static const unsigned char vol_cvt_datt[128] = {
@@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x00, 0x0f, /* 0: power-up, un-reset */
0xff, 0xff
};
+ static const unsigned char inits_ak4620[] = {
+ 0x00, 0x07, /* 0: normal */
+ 0x01, 0x00, /* 0: reset */
+ 0x01, 0x02, /* 1: RSTAD */
+ 0x01, 0x03, /* 1: RSTDA */
+ 0x01, 0x0f, /* 1: normal */
+ 0x02, 0x60, /* 2: 24bit I2S */
+ 0x03, 0x01, /* 3: deemphasis off */
+ 0x04, 0x00, /* 4: LIN muted */
+ 0x05, 0x00, /* 5: RIN muted */
+ 0x06, 0x00, /* 6: LOUT muted */
+ 0x07, 0x00, /* 7: ROUT muted */
+ 0xff, 0xff
+ };
- int chip, num_chips;
+ int chip;
const unsigned char *ptr, *inits;
unsigned char reg, data;
@@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
switch (ak->type) {
case SND_AK4524:
inits = inits_ak4524;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4524";
+ ak->total_regs = 0x08;
break;
case SND_AK4528:
inits = inits_ak4528;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4528";
+ ak->total_regs = 0x06;
break;
case SND_AK4529:
inits = inits_ak4529;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4529";
+ ak->total_regs = 0x0d;
break;
case SND_AK4355:
inits = inits_ak4355;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4355";
+ ak->total_regs = 0x0b;
break;
case SND_AK4358:
inits = inits_ak4358;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4358";
+ ak->total_regs = 0x10;
break;
case SND_AK4381:
inits = inits_ak4381;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4381";
+ ak->total_regs = 0x05;
break;
case SND_AK5365:
/* FIXME: any init sequence? */
+ ak->num_chips = 1;
+ ak->name = "ak5365";
+ ak->total_regs = 0x08;
return;
+ case SND_AK4620:
+ inits = inits_ak4620;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4620";
+ ak->total_regs = 0x08;
+ break;
default:
snd_BUG();
return;
}
- for (chip = 0; chip < num_chips; chip++) {
+ for (chip = 0; chip < ak->num_chips; chip++) {
ptr = inits;
while (*ptr != 0xff) {
reg = *ptr++;
data = *ptr++;
snd_akm4xxx_write(ak, chip, reg, data);
+ udelay(10);
}
}
}
@@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
knew.tlv.p = db_scale_linear;
break;
+ case SND_AK4620:
+ /* register 6 & 7 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
+ knew.tlv.p = db_scale_linear;
+ break;
default:
return -EINVAL;
}
@@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
static int build_adc_controls(struct snd_akm4xxx *ak)
{
- int idx, err, mixer_ch, num_stereo;
+ int idx, err, mixer_ch, num_stereo, max_steps;
struct snd_kcontrol_new knew;
mixer_ch = 0;
+ if (ak->type == SND_AK4528)
+ return 0; /* no controls */
for (idx = 0; idx < ak->num_adcs;) {
memset(&knew, 0, sizeof(knew));
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
}
/* register 4 & 5 */
if (ak->type == SND_AK5365)
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 152;
else
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 164;
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
+ AK_VOL_CVT | AK_IPGA;
knew.tlv.p = db_scale_vol_datt;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
@@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
/* register 3 */
knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break;
@@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+ int reg, val, chip;
+ for (chip = 0; chip < ak->num_chips; chip++) {
+ for (reg = 0; reg < ak->total_regs; reg++) {
+ val = snd_akm4xxx_get(ak, chip, reg);
+ snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
+ reg, val);
+ }
+ }
+}
+
+static int proc_init(struct snd_akm4xxx *ak)
+{
+ struct snd_info_entry *entry;
+ int err;
+ err = snd_card_proc_new(ak->card, ak->name, &entry);
+ if (err < 0)
+ return err;
+ snd_info_set_text_ops(entry, ak, proc_regs_read);
+ return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static int proc_init(struct snd_akm4xxx *ak) {}
+#endif
+
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
int err, num_emphs;
@@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
err = build_adc_controls(ak);
if (err < 0)
return err;
-
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1;
+ else if (ak->type == SND_AK4620)
+ num_emphs = 0;
else
num_emphs = ak->num_dacs / 2;
err = build_deemphasis(ak, num_emphs);
if (err < 0)
return err;
+ err = proc_init(ak);
+ if (err < 0)
+ return err;
return 0;
}
-
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
static int __init alsa_akm4xxx_module_init(void)
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index d31c373e076..c4c6ef73f9b 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -225,7 +225,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
case V4L2_CID_AUDIO_MUTE:
if (tea->ops->mute) {
tea->ops->mute(tea, ctrl->value);
- tea->mute = 1;
+ tea->mute = ctrl->value;
return 0;
}
}
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 51a7e3777e1..02fe81ca88f 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -372,15 +372,21 @@ config SND_SGALAXY
config SND_SSCAPE
tristate "Ensoniq SoundScape driver"
- select SND_HWDEP
select SND_MPU401_UART
select SND_WSS_LIB
+ select FW_LOADER
help
Say Y here to include support for Ensoniq SoundScape
- soundcards.
+ and Ensoniq OEM soundcards.
The PCM audio is supported on SoundScape Classic, Elite, PnP
- and VIVO cards. The MIDI support is very experimental.
+ and VIVO cards. The supported OEM cards are SPEA Media FX and
+ Reveal SC-600.
+ The MIDI support is very experimental and requires binary
+ firmware files called "scope.cod" and "sndscape.co?" where the
+ ? is digit 0, 1, 2, 3 or 4. The firmware files can be found
+ in DOS or Windows driver packages. One has to put the firmware
+ files into the /lib/firmware directory.
To compile this driver as a module, choose M here: the module
will be called snd-sscape.
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 02f79d25271..8246aae32ab 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -237,7 +237,7 @@ WSS_DOUBLE("Wavetable Capture Volume", 0,
CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
WSS_SINGLE("3D Control - Switch", 0,
CMI8330_RMUX3D, 5, 1, 1),
-WSS_SINGLE("PC Speaker Playback Volume", 0,
+WSS_SINGLE("Beep Playback Volume", 0,
CMI8330_OUTPUTVOL, 3, 3, 0),
WSS_DOUBLE("FM Playback Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
@@ -262,7 +262,7 @@ SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3,
SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
-SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+SB_SINGLE("SB Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index a076a6ce807..cc15d1d65a2 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -177,7 +177,7 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
{ .id = "CSC0437", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* Digital PC 5000 Onboard - CS4236B */
{ .id = "CSC0735", .devs = { { "CSC0000" }, { "CSC0010" } } },
- /* some uknown CS4236B */
+ /* some unknown CS4236B */
{ .id = "CSC0b35", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* Intel PR440FX Onboard sound */
{ .id = "CSC0b36", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
@@ -394,21 +394,15 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
return -EBUSY;
}
- err = snd_wss_create(card, port[dev], cport[dev],
+ err = snd_cs4236_create(card, port[dev], cport[dev],
irq[dev],
dma1[dev], dma2[dev],
WSS_HW_DETECT3, 0, &chip);
if (err < 0)
return err;
+
+ acard->chip = chip;
if (chip->hardware & WSS_HW_CS4236B_MASK) {
- snd_wss_free(chip);
- err = snd_cs4236_create(card,
- port[dev], cport[dev],
- irq[dev], dma1[dev], dma2[dev],
- WSS_HW_DETECT, 0, &chip);
- if (err < 0)
- return err;
- acard->chip = chip;
err = snd_cs4236_pcm(chip, 0, &pcm);
if (err < 0)
@@ -418,7 +412,6 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
} else {
- acard->chip = chip;
err = snd_wss_pcm(chip, 0, &pcm);
if (err < 0)
return err;
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index 38835f31298..c5adca30063 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -87,6 +87,8 @@
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
/*
*
@@ -264,7 +266,10 @@ static void snd_cs4236_resume(struct snd_wss *chip)
}
#endif /* CONFIG_PM */
-
+/*
+ * This function does no fail if the chip is not CS4236B or compatible.
+ * It just an equivalent to the snd_wss_create() then.
+ */
int snd_cs4236_create(struct snd_card *card,
unsigned long port,
unsigned long cport,
@@ -281,21 +286,17 @@ int snd_cs4236_create(struct snd_card *card,
*rchip = NULL;
if (hardware == WSS_HW_DETECT)
hardware = WSS_HW_DETECT3;
- if (cport < 0x100) {
- snd_printk(KERN_ERR "please, specify control port "
- "for CS4236+ chips\n");
- return -ENODEV;
- }
+
err = snd_wss_create(card, port, cport,
irq, dma1, dma2, hardware, hwshare, &chip);
if (err < 0)
return err;
- if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
- snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers "
- "not available, hardware=0x%x\n", chip->hardware);
- snd_device_free(card, chip);
- return -ENODEV;
+ if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) {
+ snd_printd("chip is not CS4236+, hardware=0x%x\n",
+ chip->hardware);
+ *rchip = chip;
+ return 0;
}
#if 0
{
@@ -308,9 +309,16 @@ int snd_cs4236_create(struct snd_card *card,
idx, snd_cs4236_ctrl_in(chip, idx));
}
#endif
+ if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
+ snd_printk(KERN_ERR "please, specify control port "
+ "for CS4236+ chips\n");
+ snd_device_free(card, chip);
+ return -ENODEV;
+ }
ver1 = snd_cs4236_ctrl_in(chip, 1);
ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
+ snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
+ cport, ver1, ver2);
if (ver1 != ver2) {
snd_printk(KERN_ERR "CS4236+ chip detected, but "
"control port 0x%lx is not valid\n", cport);
@@ -321,13 +329,17 @@ int snd_cs4236_create(struct snd_card *card,
snd_cs4236_ctrl_out(chip, 2, 0xff);
snd_cs4236_ctrl_out(chip, 3, 0x00);
snd_cs4236_ctrl_out(chip, 4, 0x80);
- snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE);
+ reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) |
+ IEC958_AES0_CON_EMPHASIS_NONE;
+ snd_cs4236_ctrl_out(chip, 5, reg);
snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
snd_cs4236_ctrl_out(chip, 7, 0x00);
- /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
- /* is working with this setup, other hardware should have */
- /* different signal paths and this value should be selectable */
- /* in the future */
+ /*
+ * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958
+ * output is working with this setup, other hardware should
+ * have different signal paths and this value should be
+ * selectable in the future
+ */
snd_cs4236_ctrl_out(chip, 8, 0x8c);
chip->rate_constraint = snd_cs4236_xrate;
chip->set_playback_format = snd_cs4236_playback_format;
@@ -339,9 +351,10 @@ int snd_cs4236_create(struct snd_card *card,
/* initialize extended registers */
for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
- snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
+ snd_cs4236_ext_out(chip, CS4236_I23VAL(reg),
+ snd_cs4236_ext_map[reg]);
- /* initialize compatible but more featured registers */
+ /* initialize compatible but more featured registers */
snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
@@ -387,6 +400,14 @@ int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
.get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#define CS4236_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_single, \
+ .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -490,6 +511,16 @@ static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
.get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define CS4236_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_double, \
+ .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+ (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 24) & 0xff;
@@ -560,12 +591,23 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_cs4236_info_double, \
.get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define CS4236_DOUBLE1_TLV(xname, xindex, left_reg, right_reg, shift_left, \
+ shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = snd_cs4236_info_double, \
+ .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+ (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
+
static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
@@ -619,16 +661,18 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
-#define CS4236_MASTER_DIGITAL(xname, xindex) \
+#define CS4236_MASTER_DIGITAL(xname, xindex, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_cs4236_info_double, \
.get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
- .private_value = 71 << 24 }
+ .private_value = 71 << 24, \
+ .tlv = { .p = (xtlv) } }
static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
{
return (vol < 64) ? 63 - vol : 64 + (71 - vol);
-}
+}
static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
@@ -661,11 +705,13 @@ static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct s
return change;
}
-#define CS4235_OUTPUT_ACCU(xname, xindex) \
+#define CS4235_OUTPUT_ACCU(xname, xindex, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_cs4236_info_double, \
.get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
- .private_value = 3 << 24 }
+ .private_value = 3 << 24, \
+ .tlv = { .p = (xtlv) } }
static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
{
@@ -720,41 +766,56 @@ static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_
return change;
}
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit_12db_max, -8250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_22db_max, -2400, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
static struct snd_kcontrol_new snd_cs4236_controls[] = {
CS4236_DOUBLE("Master Digital Playback Switch", 0,
CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
CS4236_DOUBLE("Master Digital Capture Switch", 0,
CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0, db_scale_7bit),
-CS4236_DOUBLE("Capture Boost Volume", 0,
- CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Boost Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+ db_scale_2bit),
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("DSP Playback Switch", 0,
CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
-CS4236_DOUBLE("DSP Playback Volume", 0,
- CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("DSP Playback Volume", 0,
+ CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("FM Playback Switch", 0,
CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("FM Playback Volume", 0,
- CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("FM Playback Volume", 0,
+ CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("Wavetable Playback Switch", 0,
CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Playback Volume", 0,
- CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+CS4236_DOUBLE_TLV("Wavetable Playback Volume", 0,
+ CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1,
+ db_scale_6bit_12db_max),
WSS_DOUBLE("Synth Playback Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Synth Volume", 0,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Synth Capture Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
WSS_DOUBLE("Synth Capture Bypass", 0,
@@ -764,14 +825,16 @@ CS4236_DOUBLE("Mic Playback Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
CS4236_DOUBLE("Mic Capture Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
-CS4236_DOUBLE("Mic Playback Boost", 0,
+CS4236_DOUBLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC,
+ 0, 0, 31, 1, db_scale_5bit_22db_max),
+CS4236_DOUBLE("Mic Playback Boost (+20dB)", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
WSS_DOUBLE("Line Playback Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Line Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Line Capture Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
WSS_DOUBLE("Line Capture Bypass", 0,
@@ -779,57 +842,63 @@ WSS_DOUBLE("Line Capture Bypass", 0,
WSS_DOUBLE("CD Playback Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("CD Volume", 0,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("CD Volume", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("CD Capture Switch", 0,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
CS4236_DOUBLE1("Mono Output Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-CS4236_DOUBLE1("Mono Playback Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+WSS_SINGLE_TLV("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1,
+ db_scale_4bit),
+WSS_SINGLE("Beep Bypass Playback Switch", 0, CS4231_MONO_CTRL, 5, 1, 0),
-WSS_DOUBLE("Capture Volume", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
+ 0, 0, 15, 0, db_scale_rec_gain),
WSS_DOUBLE("Analog Loopback Capture Switch", 0,
CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
-WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
- CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+WSS_SINGLE("Loopback Digital Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0,
+ CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1,
+ db_scale_6bit),
};
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0);
+
static struct snd_kcontrol_new snd_cs4235_controls[] = {
-WSS_DOUBLE("Master Switch", 0,
+WSS_DOUBLE("Master Playback Switch", 0,
CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
-WSS_DOUBLE("Master Volume", 0,
- CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
-
-CS4235_OUTPUT_ACCU("Playback Volume", 0),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+ CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1,
+ db_scale_5bit_6db_max),
-CS4236_DOUBLE("Master Digital Playback Switch", 0,
- CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0,
- CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
-CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+CS4235_OUTPUT_ACCU("Playback Volume", 0, db_scale_2bit_16db_max),
-WSS_DOUBLE("Master Digital Playback Switch", 1,
+WSS_DOUBLE("Synth Playback Switch", 1,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Master Digital Capture Switch", 1,
+WSS_DOUBLE("Synth Capture Switch", 1,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-WSS_DOUBLE("Master Digital Volume", 1,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Synth Volume", 1,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-CS4236_DOUBLE("Capture Volume", 0,
- CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE_TLV("Capture Volume", 0,
+ CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1,
+ db_scale_2bit),
-WSS_DOUBLE("PCM Switch", 0,
+WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Capture Switch", 0,
+ CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+WSS_DOUBLE_TLV("PCM Volume", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
@@ -842,29 +911,29 @@ CS4236_DOUBLE("Mic Capture Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
CS4236_DOUBLE("Mic Playback Switch", 0,
CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
-CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
-CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
+CS4236_SINGLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1,
+ db_scale_5bit_22db_max),
+CS4236_SINGLE("Mic Boost (+20dB)", 0, CS4236_LEFT_MIC, 5, 1, 0),
-WSS_DOUBLE("Aux Playback Switch", 0,
+WSS_DOUBLE("Line Playback Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 0,
+WSS_DOUBLE("Line Capture Switch", 0,
CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE_TLV("Line Volume", 0,
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-WSS_DOUBLE("Aux Playback Switch", 1,
+WSS_DOUBLE("CD Playback Switch", 1,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Capture Switch", 1,
+WSS_DOUBLE("CD Capture Switch", 1,
CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-WSS_DOUBLE("Aux Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-
-CS4236_DOUBLE1("Master Mono Switch", 0,
- CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+WSS_DOUBLE_TLV("CD Volume", 1,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
-CS4236_DOUBLE1("Mono Switch", 0,
+CS4236_DOUBLE1("Beep Playback Switch", 0,
CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
WSS_DOUBLE("Analog Loopback Switch", 0,
CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 4c6e14f87f2..c76bb00c9d1 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -982,7 +982,7 @@ ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0
ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
-ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
+ES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
{
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 8cfbff73a83..9a43baae725 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -102,8 +102,6 @@
struct snd_es18xx {
unsigned long port; /* port of ESS chip */
- unsigned long mpu_port; /* MPU-401 port of ESS chip */
- unsigned long fm_port; /* FM port */
unsigned long ctrl_port; /* Control port of ESS chip */
struct resource *res_port;
struct resource *res_mpu_port;
@@ -116,12 +114,9 @@ struct snd_es18xx {
unsigned short audio2_vol; /* volume level of audio2 */
unsigned short active; /* active channel mask */
- unsigned int dma1_size;
- unsigned int dma2_size;
unsigned int dma1_shift;
unsigned int dma2_shift;
- struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *playback_a_substream;
struct snd_pcm_substream *capture_a_substream;
@@ -136,14 +131,9 @@ struct snd_es18xx {
spinlock_t reg_lock;
spinlock_t mixer_lock;
- spinlock_t ctrl_lock;
#ifdef CONFIG_PM
unsigned char pm_reg;
#endif
-};
-
-struct snd_audiodrive {
- struct snd_es18xx *chip;
#ifdef CONFIG_PNP
struct pnp_dev *dev;
struct pnp_dev *devc;
@@ -359,7 +349,7 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
}
-static int snd_es18xx_reset(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_reset(struct snd_es18xx *chip)
{
int i;
outb(0x03, chip->port + 0x06);
@@ -495,8 +485,6 @@ static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma2_size = size;
-
snd_es18xx_rate_set(chip, substream, DAC2);
/* Transfer Count Reload */
@@ -596,8 +584,6 @@ static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma1_size = size;
-
snd_es18xx_reset_fifo(chip);
/* Set stereo/mono */
@@ -664,8 +650,6 @@ static int snd_es18xx_playback2_prepare(struct snd_es18xx *chip,
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- chip->dma1_size = size;
-
snd_es18xx_reset_fifo(chip);
/* Set stereo/mono */
@@ -755,7 +739,8 @@ static int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream,
static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
{
- struct snd_es18xx *chip = dev_id;
+ struct snd_card *card = dev_id;
+ struct snd_es18xx *chip = card->private_data;
unsigned char status;
if (chip->caps & ES18XX_CONTROL) {
@@ -805,12 +790,16 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
int split = 0;
if (chip->caps & ES18XX_HWV) {
split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->hw_switch->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->hw_volume->id);
}
if (!split) {
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_switch->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
}
/* ack interrupt */
snd_es18xx_mixer_write(chip, 0x66, 0x00);
@@ -821,17 +810,18 @@ static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
int pos;
if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
if (!(chip->active & DAC2))
return 0;
- pos = snd_dma_pointer(chip->dma2, chip->dma2_size);
+ pos = snd_dma_pointer(chip->dma2, size);
return pos >> chip->dma2_shift;
} else {
if (!(chip->active & DAC1))
return 0;
- pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+ pos = snd_dma_pointer(chip->dma1, size);
return pos >> chip->dma1_shift;
}
}
@@ -839,11 +829,12 @@ static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *s
static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
+ unsigned int size = snd_pcm_lib_buffer_bytes(substream);
int pos;
if (!(chip->active & ADC1))
return 0;
- pos = snd_dma_pointer(chip->dma1, chip->dma1_size);
+ pos = snd_dma_pointer(chip->dma1, size);
return pos >> chip->dma1_shift;
}
@@ -974,9 +965,6 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts4Source[4] = {
- "Mic", "CD", "Line", "Master"
- };
static char *texts5Source[5] = {
"Mic", "CD", "Line", "Master", "Mix"
};
@@ -994,7 +982,8 @@ static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
uinfo->value.enumerated.items = 4;
if (uinfo->value.enumerated.item > 3)
uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts4Source[uinfo->value.enumerated.item]);
+ strcpy(uinfo->value.enumerated.name,
+ texts5Source[uinfo->value.enumerated.item]);
break;
case 0x1887:
case 0x1888:
@@ -1313,7 +1302,7 @@ ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
* The chipset specific mixer controls
*/
static struct snd_kcontrol_new snd_es18xx_opt_speaker =
- ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0);
+ ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1378,11 +1367,9 @@ ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
{
int data;
- unsigned long flags;
- spin_lock_irqsave(&chip->ctrl_lock, flags);
+
outb(reg, chip->ctrl_port);
data = inb(chip->ctrl_port + 1);
- spin_unlock_irqrestore(&chip->ctrl_lock, flags);
return data;
}
@@ -1398,7 +1385,9 @@ static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
#endif
}
-static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
int mask = 0;
@@ -1412,15 +1401,15 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
if (chip->caps & ES18XX_CONTROL) {
/* Hardware volume IRQ */
snd_es18xx_config_write(chip, 0x27, chip->irq);
- if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
/* FM I/O */
- snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);
- snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);
+ snd_es18xx_config_write(chip, 0x62, fm_port >> 8);
+ snd_es18xx_config_write(chip, 0x63, fm_port & 0xff);
}
- if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+ if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
/* MPU-401 I/O */
- snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);
- snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);
+ snd_es18xx_config_write(chip, 0x64, mpu_port >> 8);
+ snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff);
/* MPU-401 IRQ */
snd_es18xx_config_write(chip, 0x28, chip->irq);
}
@@ -1507,11 +1496,12 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
snd_es18xx_mixer_write(chip, 0x7A, 0x68);
/* Enable and set hardware volume interrupt */
snd_es18xx_mixer_write(chip, 0x64, 0x06);
- if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+ if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
/* MPU401 share irq with audio
Joystick enabled
FM enabled */
- snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);
+ snd_es18xx_mixer_write(chip, 0x40,
+ 0x43 | (mpu_port & 0xf0) >> 1);
}
snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
}
@@ -1629,7 +1619,9 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
return 0;
}
-static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
if (snd_es18xx_identify(chip) < 0) {
snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
@@ -1650,8 +1642,6 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
break;
case 0x1887:
- chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
- break;
case 0x1888:
chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
break;
@@ -1666,7 +1656,7 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip)
if (chip->dma1 == chip->dma2)
chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
- return snd_es18xx_initialize(chip);
+ return snd_es18xx_initialize(chip, mpu_port, fm_port);
}
static struct snd_pcm_ops snd_es18xx_playback_ops = {
@@ -1691,8 +1681,10 @@ static struct snd_pcm_ops snd_es18xx_capture_ops = {
.pointer = snd_es18xx_capture_pointer,
};
-static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
+ struct snd_pcm **rpcm)
{
+ struct snd_es18xx *chip = card->private_data;
struct snd_pcm *pcm;
char str[16];
int err;
@@ -1701,9 +1693,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
*rpcm = NULL;
sprintf(str, "ES%x", chip->version);
if (chip->caps & ES18XX_PCM2)
- err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);
+ err = snd_pcm_new(card, str, device, 2, 1, &pcm);
else
- err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);
+ err = snd_pcm_new(card, str, device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1734,10 +1726,9 @@ static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct
#ifdef CONFIG_PM
static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip = acard->chip;
+ struct snd_es18xx *chip = card->private_data;
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
@@ -1752,24 +1743,25 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
static int snd_es18xx_resume(struct snd_card *card)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip = acard->chip;
+ struct snd_es18xx *chip = card->private_data;
/* restore PM register, we won't wake till (not 0x07) i/o activity though */
snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif /* CONFIG_PM */
-static int snd_es18xx_free(struct snd_es18xx *chip)
+static int snd_es18xx_free(struct snd_card *card)
{
+ struct snd_es18xx *chip = card->private_data;
+
release_and_free_resource(chip->res_port);
release_and_free_resource(chip->res_ctrl_port);
release_and_free_resource(chip->res_mpu_port);
if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
+ free_irq(chip->irq, (void *) card);
if (chip->dma1 >= 0) {
disable_dma(chip->dma1);
free_dma(chip->dma1);
@@ -1778,93 +1770,82 @@ static int snd_es18xx_free(struct snd_es18xx *chip)
disable_dma(chip->dma2);
free_dma(chip->dma2);
}
- kfree(chip);
return 0;
}
static int snd_es18xx_dev_free(struct snd_device *device)
{
- struct snd_es18xx *chip = device->device_data;
- return snd_es18xx_free(chip);
+ return snd_es18xx_free(device->card);
}
static int __devinit snd_es18xx_new_device(struct snd_card *card,
unsigned long port,
unsigned long mpu_port,
unsigned long fm_port,
- int irq, int dma1, int dma2,
- struct snd_es18xx ** rchip)
+ int irq, int dma1, int dma2)
{
- struct snd_es18xx *chip;
+ struct snd_es18xx *chip = card->private_data;
static struct snd_device_ops ops = {
.dev_free = snd_es18xx_dev_free,
};
int err;
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
- spin_lock_init(&chip->ctrl_lock);
- chip->card = card;
chip->port = port;
- chip->mpu_port = mpu_port;
- chip->fm_port = fm_port;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
chip->audio2_vol = 0x00;
chip->active = 0;
- if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) {
- snd_es18xx_free(chip);
+ chip->res_port = request_region(port, 16, "ES18xx");
+ if (chip->res_port == NULL) {
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx", (void *) chip)) {
- snd_es18xx_free(chip);
+ if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
+ (void *) card)) {
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
if (request_dma(dma1, "ES18xx DMA 1")) {
- snd_es18xx_free(chip);
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
- snd_es18xx_free(chip);
+ snd_es18xx_free(card);
snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
- if (snd_es18xx_probe(chip) < 0) {
- snd_es18xx_free(chip);
- return -ENODEV;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es18xx_free(chip);
+ if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) {
+ snd_es18xx_free(card);
+ return -ENODEV;
+ }
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_es18xx_free(card);
return err;
}
- *rchip = chip;
return 0;
}
-static int __devinit snd_es18xx_mixer(struct snd_es18xx *chip)
+static int __devinit snd_es18xx_mixer(struct snd_card *card)
{
- struct snd_card *card;
+ struct snd_es18xx *chip = card->private_data;
int err;
unsigned int idx;
- card = chip->card;
-
strcpy(card->mixername, chip->pcm->name);
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
@@ -1986,7 +1967,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
#ifndef CONFIG_PNP
@@ -2063,11 +2044,11 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
return 0;
}
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
struct pnp_dev *pdev)
{
- acard->dev = pdev;
- if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ chip->dev = pdev;
+ if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
return 0;
}
@@ -2093,26 +2074,26 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
-static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
- acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
- if (acard->dev == NULL)
+ chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (chip->dev == NULL)
return -EBUSY;
- acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
- if (acard->devc == NULL)
+ chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (chip->devc == NULL)
return -EBUSY;
/* Control port initialization */
- if (pnp_activate_dev(acard->devc) < 0) {
+ if (pnp_activate_dev(chip->devc) < 0) {
snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
return -EAGAIN;
}
snd_printdd("pnp: port=0x%llx\n",
- (unsigned long long)pnp_port_start(acard->devc, 0));
- if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ (unsigned long long)pnp_port_start(chip->devc, 0));
+ if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
return 0;
@@ -2128,24 +2109,20 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
{
return snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_audiodrive), cardp);
+ sizeof(struct snd_es18xx), cardp);
}
static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
{
- struct snd_audiodrive *acard = card->private_data;
- struct snd_es18xx *chip;
+ struct snd_es18xx *chip = card->private_data;
struct snd_opl3 *opl3;
int err;
- if ((err = snd_es18xx_new_device(card,
- port[dev],
- mpu_port[dev],
- fm_port[dev],
- irq[dev], dma1[dev], dma2[dev],
- &chip)) < 0)
+ err = snd_es18xx_new_device(card,
+ port[dev], mpu_port[dev], fm_port[dev],
+ irq[dev], dma1[dev], dma2[dev]);
+ if (err < 0)
return err;
- acard->chip = chip;
sprintf(card->driver, "ES%x", chip->version);
@@ -2161,26 +2138,32 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
chip->port,
irq[dev], dma1[dev]);
- if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0)
+ err = snd_es18xx_pcm(card, 0, NULL);
+ if (err < 0)
return err;
- if ((err = snd_es18xx_mixer(chip)) < 0)
+ err = snd_es18xx_mixer(card);
+ if (err < 0)
return err;
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
- if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING PFX "opl3 not detected at 0x%lx\n", chip->fm_port);
+ if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+ OPL3_HW_OPL3, 0, &opl3) < 0) {
+ snd_printk(KERN_WARNING PFX
+ "opl3 not detected at 0x%lx\n",
+ fm_port[dev]);
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
}
}
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
- chip->mpu_port, 0,
- irq[dev], 0,
- &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
+ mpu_port[dev], 0,
+ irq[dev], 0, &chip->rmidi);
+ if (err < 0)
return err;
}
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index 661205c4dce..af888a022fc 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -127,7 +127,8 @@ static struct snd_gf1_mem_block *snd_gf1_mem_share(struct snd_gf1_mem * alloc,
!share_id[2] && !share_id[3])
return NULL;
for (block = alloc->first; block; block = block->next)
- if (!memcmp(share_id, block->share_id, sizeof(share_id)))
+ if (!memcmp(share_id, block->share_id,
+ sizeof(block->share_id)))
return block;
return NULL;
}
diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c
index cb9aa4c4edd..4be562b2cf2 100644
--- a/sound/isa/msnd/msnd_midi.c
+++ b/sound/isa/msnd/msnd_midi.c
@@ -162,7 +162,7 @@ int snd_msndmidi_new(struct snd_card *card, int device)
err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
if (err < 0)
return err;
- mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL);
+ mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
if (mpu == NULL) {
snd_device_free(card, rmidi);
return -ENOMEM;
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 02e30d7c6a9..b865e45a8f9 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
@@ -40,7 +41,7 @@
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#include "miro.h"
+#include <sound/aci.h>
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
MODULE_LICENSE("GPL");
@@ -60,6 +61,9 @@ static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
+#ifdef CONFIG_PNP
+static int isapnp = 1; /* Enable ISA PnP detection */
+#endif
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
@@ -83,6 +87,10 @@ module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
module_param(ide, int, 0444);
MODULE_PARM_DESC(ide, "enable ide port");
+#ifdef CONFIG_PNP
+module_param(isapnp, bool, 0444);
+MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
+#endif
#define OPTi9XX_HW_DETECT 0
#define OPTi9XX_HW_82C928 1
@@ -96,7 +104,6 @@ MODULE_PARM_DESC(ide, "enable ide port");
#define OPTi9XX_MC_REG(n) n
-
struct snd_miro {
unsigned short hardware;
unsigned char password;
@@ -110,7 +117,6 @@ struct snd_miro {
unsigned long pwd_reg;
spinlock_t lock;
- struct snd_card *card;
struct snd_pcm *pcm;
long wss_base;
@@ -118,42 +124,48 @@ struct snd_miro {
int dma1;
int dma2;
- long fm_port;
-
long mpu_port;
int mpu_irq;
- unsigned long aci_port;
- int aci_vendor;
- int aci_product;
- int aci_version;
- int aci_amp;
- int aci_preamp;
- int aci_solomode;
-
- struct mutex aci_mutex;
+ struct snd_miro_aci *aci;
};
-static void snd_miro_proc_init(struct snd_miro * miro);
+static struct snd_miro_aci aci_device;
static char * snd_opti9xx_names[] = {
- "unkown",
+ "unknown",
"82C928", "82C929",
"82C924", "82C925",
"82C930", "82C931", "82C933"
};
+static int snd_miro_pnp_is_probed;
+
+#ifdef CONFIG_PNP
+
+static struct pnp_card_device_id snd_miro_pnpids[] = {
+ /* PCM20 and PCM12 in PnP mode */
+ { .id = "MIR0924",
+ .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
+ { .id = "" }
+};
+
+MODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
+
+#endif /* CONFIG_PNP */
+
/*
* ACI control
*/
-static int aci_busy_wait(struct snd_miro * miro)
+static int aci_busy_wait(struct snd_miro_aci *aci)
{
long timeout;
unsigned char byte;
- for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) {
- if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) {
+ for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) {
+ byte = inb(aci->aci_port + ACI_REG_BUSY);
+ if ((byte & 1) == 0) {
if (timeout >= ACI_MINTIME)
snd_printd("aci ready in round %ld.\n",
timeout-ACI_MINTIME);
@@ -179,10 +191,10 @@ static int aci_busy_wait(struct snd_miro * miro)
return -EBUSY;
}
-static inline int aci_write(struct snd_miro * miro, unsigned char byte)
+static inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
{
- if (aci_busy_wait(miro) >= 0) {
- outb(byte, miro->aci_port + ACI_REG_COMMAND);
+ if (aci_busy_wait(aci) >= 0) {
+ outb(byte, aci->aci_port + ACI_REG_COMMAND);
return 0;
} else {
snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
@@ -190,12 +202,12 @@ static inline int aci_write(struct snd_miro * miro, unsigned char byte)
}
}
-static inline int aci_read(struct snd_miro * miro)
+static inline int aci_read(struct snd_miro_aci *aci)
{
unsigned char byte;
- if (aci_busy_wait(miro) >= 0) {
- byte=inb(miro->aci_port + ACI_REG_STATUS);
+ if (aci_busy_wait(aci) >= 0) {
+ byte = inb(aci->aci_port + ACI_REG_STATUS);
return byte;
} else {
snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
@@ -203,39 +215,49 @@ static inline int aci_read(struct snd_miro * miro)
}
}
-static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3)
+int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3)
{
int write[] = {write1, write2, write3};
int value, i;
- if (mutex_lock_interruptible(&miro->aci_mutex))
+ if (mutex_lock_interruptible(&aci->aci_mutex))
return -EINTR;
for (i=0; i<3; i++) {
if (write[i]< 0 || write[i] > 255)
break;
else {
- value = aci_write(miro, write[i]);
+ value = aci_write(aci, write[i]);
if (value < 0)
goto out;
}
}
- value = aci_read(miro);
+ value = aci_read(aci);
-out: mutex_unlock(&miro->aci_mutex);
+out: mutex_unlock(&aci->aci_mutex);
return value;
}
+EXPORT_SYMBOL(snd_aci_cmd);
+
+static int aci_getvalue(struct snd_miro_aci *aci, unsigned char index)
+{
+ return snd_aci_cmd(aci, ACI_STATUS, index, -1);
+}
-static int aci_getvalue(struct snd_miro * miro, unsigned char index)
+static int aci_setvalue(struct snd_miro_aci *aci, unsigned char index,
+ int value)
{
- return aci_cmd(miro, ACI_STATUS, index, -1);
+ return snd_aci_cmd(aci, index, value, -1);
}
-static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value)
+struct snd_miro_aci *snd_aci_get_aci(void)
{
- return aci_cmd(miro, index, value, -1);
+ if (aci_device.aci_port == 0)
+ return NULL;
+ return &aci_device;
}
+EXPORT_SYMBOL(snd_aci_get_aci);
/*
* MIXER part
@@ -249,8 +271,10 @@ static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int value;
- if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) {
- snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value);
+ value = aci_getvalue(miro->aci, ACI_S_GENERAL);
+ if (value < 0) {
+ snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
+ value);
return value;
}
@@ -267,13 +291,15 @@ static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
value = !(ucontrol->value.integer.value[0]);
- if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) {
- snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error);
+ error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
+ if (error < 0) {
+ snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
+ error);
return error;
}
- change = (value != miro->aci_solomode);
- miro->aci_solomode = value;
+ change = (value != miro->aci->aci_solomode);
+ miro->aci->aci_solomode = value;
return change;
}
@@ -295,7 +321,7 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int value;
- if (miro->aci_version <= 176) {
+ if (miro->aci->aci_version <= 176) {
/*
OSS says it's not readable with versions < 176.
@@ -303,12 +329,14 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
which is a PCM12 with aci_version = 176.
*/
- ucontrol->value.integer.value[0] = miro->aci_preamp;
+ ucontrol->value.integer.value[0] = miro->aci->aci_preamp;
return 0;
}
- if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) {
- snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value);
+ value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
+ if (value < 0) {
+ snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
+ value);
return value;
}
@@ -325,13 +353,15 @@ static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
value = ucontrol->value.integer.value[0];
- if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) {
- snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error);
+ error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
+ if (error < 0) {
+ snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
+ error);
return error;
}
- change = (value != miro->aci_preamp);
- miro->aci_preamp = value;
+ change = (value != miro->aci->aci_preamp);
+ miro->aci->aci_preamp = value;
return change;
}
@@ -342,7 +372,7 @@ static int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = miro->aci_amp;
+ ucontrol->value.integer.value[0] = miro->aci->aci_amp;
return 0;
}
@@ -355,13 +385,14 @@ static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
value = ucontrol->value.integer.value[0];
- if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) {
+ error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
+ if (error < 0) {
snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
return error;
}
- change = (value != miro->aci_amp);
- miro->aci_amp = value;
+ change = (value != miro->aci->aci_amp);
+ miro->aci->aci_amp = value;
return change;
}
@@ -410,12 +441,14 @@ static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
int right_reg = kcontrol->private_value & 0xff;
int left_reg = right_reg + 1;
- if ((right_val = aci_getvalue(miro, right_reg)) < 0) {
+ right_val = aci_getvalue(miro->aci, right_reg);
+ if (right_val < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
return right_val;
}
- if ((left_val = aci_getvalue(miro, left_reg)) < 0) {
+ left_val = aci_getvalue(miro->aci, left_reg);
+ if (left_val < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
return left_val;
}
@@ -451,6 +484,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
+ struct snd_miro_aci *aci = miro->aci;
int left, right, left_old, right_old;
int setreg_left, setreg_right, getreg_left, getreg_right;
int change, error;
@@ -459,21 +493,21 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
right = ucontrol->value.integer.value[1];
setreg_right = (kcontrol->private_value >> 8) & 0xff;
- if (setreg_right == ACI_SET_MASTER) {
- setreg_left = setreg_right + 1;
- } else {
- setreg_left = setreg_right + 8;
- }
+ setreg_left = setreg_right + 8;
+ if (setreg_right == ACI_SET_MASTER)
+ setreg_left -= 7;
getreg_right = kcontrol->private_value & 0xff;
getreg_left = getreg_right + 1;
- if ((left_old = aci_getvalue(miro, getreg_left)) < 0) {
+ left_old = aci_getvalue(aci, getreg_left);
+ if (left_old < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
return left_old;
}
- if ((right_old = aci_getvalue(miro, getreg_right)) < 0) {
+ right_old = aci_getvalue(aci, getreg_right);
+ if (right_old < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
return right_old;
}
@@ -492,13 +526,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
right_old = 0x80 - right_old;
if (left >= 0) {
- if ((error = aci_setvalue(miro, setreg_left, left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
left, error);
return error;
}
} else {
- if ((error = aci_setvalue(miro, setreg_left, 0x80 - left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, 0x80 - left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - left, error);
return error;
@@ -506,13 +542,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
}
if (right >= 0) {
- if ((error = aci_setvalue(miro, setreg_right, right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
right, error);
return error;
}
} else {
- if ((error = aci_setvalue(miro, setreg_right, 0x80 - right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, 0x80 - right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - right, error);
return error;
@@ -530,12 +568,14 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
left_old = 0x20 - left_old;
right_old = 0x20 - right_old;
- if ((error = aci_setvalue(miro, setreg_left, 0x20 - left)) < 0) {
+ error = aci_setvalue(aci, setreg_left, 0x20 - left);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x20 - left, error);
return error;
}
- if ((error = aci_setvalue(miro, setreg_right, 0x20 - right)) < 0) {
+ error = aci_setvalue(aci, setreg_right, 0x20 - right);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x20 - right, error);
return error;
@@ -633,11 +673,13 @@ static unsigned char aci_init_values[][2] __devinitdata = {
static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
{
int idx, error;
+ struct snd_miro_aci *aci = miro->aci;
/* enable WSS on PCM1 */
- if ((miro->aci_product == 'A') && wss) {
- if ((error = aci_setvalue(miro, ACI_SET_WSS, wss)) < 0) {
+ if ((aci->aci_product == 'A') && wss) {
+ error = aci_setvalue(aci, ACI_SET_WSS, wss);
+ if (error < 0) {
snd_printk(KERN_ERR "enabling WSS mode failed\n");
return error;
}
@@ -646,7 +688,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
/* enable IDE port */
if (ide) {
- if ((error = aci_setvalue(miro, ACI_SET_IDE, ide)) < 0) {
+ error = aci_setvalue(aci, ACI_SET_IDE, ide);
+ if (error < 0) {
snd_printk(KERN_ERR "enabling IDE port failed\n");
return error;
}
@@ -654,32 +697,31 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
/* set common aci values */
- for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++)
- if ((error = aci_setvalue(miro, aci_init_values[idx][0],
- aci_init_values[idx][1])) < 0) {
+ for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) {
+ error = aci_setvalue(aci, aci_init_values[idx][0],
+ aci_init_values[idx][1]);
+ if (error < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
aci_init_values[idx][0], error);
return error;
}
-
- miro->aci_amp = 0;
- miro->aci_preamp = 0;
- miro->aci_solomode = 1;
+ }
+ aci->aci_amp = 0;
+ aci->aci_preamp = 0;
+ aci->aci_solomode = 1;
return 0;
}
-static int __devinit snd_miro_mixer(struct snd_miro *miro)
+static int __devinit snd_miro_mixer(struct snd_card *card,
+ struct snd_miro *miro)
{
- struct snd_card *card;
unsigned int idx;
int err;
- if (snd_BUG_ON(!miro || !miro->card))
+ if (snd_BUG_ON(!miro || !card))
return -EINVAL;
- card = miro->card;
-
switch (miro->hardware) {
case OPTi9XX_HW_82C924:
strcpy(card->mixername, "ACI & OPTi924");
@@ -697,7 +739,8 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
return err;
}
- if ((miro->aci_product == 'A') || (miro->aci_product == 'B')) {
+ if ((miro->aci->aci_product == 'A') ||
+ (miro->aci->aci_product == 'B')) {
/* PCM1/PCM12 with power-amp and Line 2 */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
return err;
@@ -705,16 +748,17 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)
return err;
}
- if ((miro->aci_product == 'B') || (miro->aci_product == 'C')) {
+ if ((miro->aci->aci_product == 'B') ||
+ (miro->aci->aci_product == 'C')) {
/* PCM12/PCM20 with mic-preamp */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
return err;
- if (miro->aci_version >= 176)
+ if (miro->aci->aci_version >= 176)
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
return err;
}
- if (miro->aci_product == 'C') {
+ if (miro->aci->aci_product == 'C') {
/* PCM20 with radio and 7 band equalizer */
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
return err;
@@ -757,21 +801,26 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
- chip->fm_port = -1;
chip->mpu_port = -1;
chip->mpu_irq = -1;
+ chip->pwd_reg = 3;
+
+#ifdef CONFIG_PNP
+ if (isapnp && chip->mc_base)
+ /* PnP resource gives the least 10 bits */
+ chip->mc_base |= 0xc00;
+ else
+#endif
+ chip->mc_base = 0xf8c;
+
switch (hardware) {
case OPTi9XX_HW_82C929:
- chip->mc_base = 0xf8c;
chip->password = 0xe3;
- chip->pwd_reg = 3;
break;
case OPTi9XX_HW_82C924:
- chip->mc_base = 0xf8c;
chip->password = 0xe5;
- chip->pwd_reg = 3;
break;
default:
@@ -853,14 +902,15 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct snd_miro *miro = (struct snd_miro *) entry->private_data;
+ struct snd_miro_aci *aci = miro->aci;
char* model = "unknown";
/* miroSOUND PCM1 pro, early PCM12 */
if ((miro->hardware == OPTi9XX_HW_82C929) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'A')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'A')) {
+ switch (aci->aci_version) {
case 3:
model = "miroSOUND PCM1 pro";
break;
@@ -873,9 +923,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
if ((miro->hardware == OPTi9XX_HW_82C924) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'B')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'B')) {
+ switch (aci->aci_version) {
case 4:
model = "miroSOUND PCM12";
break;
@@ -891,9 +941,9 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
/* miroSOUND PCM20 radio */
if ((miro->hardware == OPTi9XX_HW_82C924) &&
- (miro->aci_vendor == 'm') &&
- (miro->aci_product == 'C')) {
- switch(miro->aci_version) {
+ (aci->aci_vendor == 'm') &&
+ (aci->aci_product == 'C')) {
+ switch (aci->aci_version) {
case 7:
model = "miroSOUND PCM20 radio (Rev. E)";
break;
@@ -917,17 +967,17 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, "ACI information:\n");
snd_iprintf(buffer, " vendor : ");
- switch(miro->aci_vendor) {
+ switch (aci->aci_vendor) {
case 'm':
snd_iprintf(buffer, "Miro\n");
break;
default:
- snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_vendor);
+ snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor);
break;
}
snd_iprintf(buffer, " product : ");
- switch(miro->aci_product) {
+ switch (aci->aci_product) {
case 'A':
snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
break;
@@ -938,26 +988,27 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
break;
default:
- snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_product);
+ snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product);
break;
}
snd_iprintf(buffer, " firmware: %d (0x%x)\n",
- miro->aci_version, miro->aci_version);
+ aci->aci_version, aci->aci_version);
snd_iprintf(buffer, " port : 0x%lx-0x%lx\n",
- miro->aci_port, miro->aci_port+2);
+ aci->aci_port, aci->aci_port+2);
snd_iprintf(buffer, " wss : 0x%x\n", wss);
snd_iprintf(buffer, " ide : 0x%x\n", ide);
- snd_iprintf(buffer, " solomode: 0x%x\n", miro->aci_solomode);
- snd_iprintf(buffer, " amp : 0x%x\n", miro->aci_amp);
- snd_iprintf(buffer, " preamp : 0x%x\n", miro->aci_preamp);
+ snd_iprintf(buffer, " solomode: 0x%x\n", aci->aci_solomode);
+ snd_iprintf(buffer, " amp : 0x%x\n", aci->aci_amp);
+ snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp);
}
-static void __devinit snd_miro_proc_init(struct snd_miro * miro)
+static void __devinit snd_miro_proc_init(struct snd_card *card,
+ struct snd_miro *miro)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(miro->card, "miro", &entry))
+ if (!snd_card_proc_new(card, "miro", &entry))
snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
}
@@ -974,37 +1025,40 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
unsigned char mpu_irq_bits;
unsigned long flags;
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
+ snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+
switch (chip->hardware) {
case OPTi9XX_HW_82C924:
snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
case OPTi9XX_HW_82C929:
/* untested init commands for OPTi929 */
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
- snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
default:
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
- switch (chip->wss_base) {
- case 0x530:
+ /* PnP resource says it decodes only 10 bits of address */
+ switch (chip->wss_base & 0x3ff) {
+ case 0x130:
+ chip->wss_base = 0x530;
wss_base_bits = 0x00;
break;
- case 0x604:
+ case 0x204:
+ chip->wss_base = 0x604;
wss_base_bits = 0x03;
break;
- case 0xe80:
+ case 0x280:
+ chip->wss_base = 0xe80;
wss_base_bits = 0x01;
break;
- case 0xf40:
+ case 0x340:
+ chip->wss_base = 0xf40;
wss_base_bits = 0x02;
break;
default:
@@ -1122,75 +1176,92 @@ __skip_mpu:
return 0;
}
+static int __devinit snd_miro_opti_check(struct snd_miro *chip)
+{
+ unsigned char value;
+
+ chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
+ "OPTi9xx MC");
+ if (chip->res_mc_base == NULL)
+ return -ENOMEM;
+
+ value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
+ if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
+ if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
+ return 0;
+
+ release_and_free_resource(chip->res_mc_base);
+ chip->res_mc_base = NULL;
+
+ return -ENODEV;
+}
+
static int __devinit snd_card_miro_detect(struct snd_card *card,
struct snd_miro *chip)
{
int i, err;
- unsigned char value;
for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
if ((err = snd_miro_init(chip, i)) < 0)
return err;
- if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
- continue;
-
- value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
- if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
- if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
- return 1;
-
- release_and_free_resource(chip->res_mc_base);
- chip->res_mc_base = NULL;
-
+ err = snd_miro_opti_check(chip);
+ if (err == 0)
+ return 1;
}
return -ENODEV;
}
static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
- struct snd_miro * miro)
+ struct snd_miro *miro)
{
unsigned char regval;
int i;
+ struct snd_miro_aci *aci = &aci_device;
+
+ miro->aci = aci;
- mutex_init(&miro->aci_mutex);
+ mutex_init(&aci->aci_mutex);
/* get ACI port from OPTi9xx MC 4 */
- miro->mc_base = 0xf8c;
regval=inb(miro->mc_base + 4);
- miro->aci_port = (regval & 0x10) ? 0x344: 0x354;
+ aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
- if ((miro->res_aci_port = request_region(miro->aci_port, 3, "miro aci")) == NULL) {
+ miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
+ if (miro->res_aci_port == NULL) {
snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
- miro->aci_port, miro->aci_port+2);
+ aci->aci_port, aci->aci_port+2);
return -ENOMEM;
}
/* force ACI into a known state */
for (i = 0; i < 3; i++)
- if (aci_cmd(miro, ACI_ERROR_OP, -1, -1) < 0) {
+ if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
snd_printk(KERN_ERR "can't force aci into known state.\n");
return -ENXIO;
}
- if ((miro->aci_vendor=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0 ||
- (miro->aci_product=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0) {
- snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", miro->aci_port);
+ aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+ aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
+ if (aci->aci_vendor < 0 || aci->aci_product < 0) {
+ snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
- if ((miro->aci_version=aci_cmd(miro, ACI_READ_VERSION, -1, -1)) < 0) {
+ aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
+ if (aci->aci_version < 0) {
snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
- miro->aci_port);
+ aci->aci_port);
return -ENXIO;
}
- if (aci_cmd(miro, ACI_INIT, -1, -1) < 0 ||
- aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
- aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
+ if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
+ snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
+ snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
snd_printk(KERN_ERR "can't initialize aci.\n");
return -ENXIO;
}
@@ -1201,157 +1272,80 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
static void snd_card_miro_free(struct snd_card *card)
{
struct snd_miro *miro = card->private_data;
-
+
release_and_free_resource(miro->res_aci_port);
+ if (miro->aci)
+ miro->aci->aci_port = 0;
release_and_free_resource(miro->res_mc_base);
}
-static int __devinit snd_miro_match(struct device *devptr, unsigned int n)
-{
- return 1;
-}
-
-static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
+static int __devinit snd_miro_probe(struct snd_card *card)
{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
- static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
- static int possible_irqs[] = {11, 9, 10, 7, -1};
- static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
- static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
-
int error;
- struct snd_miro *miro;
+ struct snd_miro *miro = card->private_data;
struct snd_wss *codec;
struct snd_timer *timer;
- struct snd_card *card;
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
- error = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_miro), &card);
- if (error < 0)
- return error;
-
- card->private_free = snd_card_miro_free;
- miro = card->private_data;
- miro->card = card;
-
- if ((error = snd_card_miro_aci_detect(card, miro)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect aci chip\n");
- return -ENODEV;
+ if (!miro->res_mc_base) {
+ miro->res_mc_base = request_region(miro->mc_base,
+ miro->mc_base_size,
+ "miro (OPTi9xx MC)");
+ if (miro->res_mc_base == NULL) {
+ snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
+ return -ENOMEM;
+ }
}
- /* init proc interface */
- snd_miro_proc_init(miro);
-
- if ((error = snd_card_miro_detect(card, miro)) < 0) {
+ error = snd_card_miro_aci_detect(card, miro);
+ if (error < 0) {
snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+ snd_printk(KERN_ERR "unable to detect aci chip\n");
return -ENODEV;
}
- if (! miro->res_mc_base &&
- (miro->res_mc_base = request_region(miro->mc_base, miro->mc_base_size,
- "miro (OPTi9xx MC)")) == NULL) {
- snd_card_free(card);
- snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
- return -ENOMEM;
- }
-
miro->wss_base = port;
- miro->fm_port = fm_port;
miro->mpu_port = mpu_port;
miro->irq = irq;
miro->mpu_irq = mpu_irq;
miro->dma1 = dma1;
miro->dma2 = dma2;
- if (miro->wss_base == SNDRV_AUTO_PORT) {
- if ((miro->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
- return -EBUSY;
- }
- }
-
- if (miro->mpu_port == SNDRV_AUTO_PORT) {
- if ((miro->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
- return -EBUSY;
- }
- }
- if (miro->irq == SNDRV_AUTO_IRQ) {
- if ((miro->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
- return -EBUSY;
- }
- }
- if (miro->mpu_irq == SNDRV_AUTO_IRQ) {
- if ((miro->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
- return -EBUSY;
- }
- }
- if (miro->dma1 == SNDRV_AUTO_DMA) {
- if ((miro->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
- return -EBUSY;
- }
- }
- if (miro->dma2 == SNDRV_AUTO_DMA) {
- if ((miro->dma2 = snd_legacy_find_free_dma(possible_dma2s[miro->dma1 % 4])) < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
- return -EBUSY;
- }
- }
+ /* init proc interface */
+ snd_miro_proc_init(card, miro);
error = snd_miro_configure(miro);
- if (error) {
- snd_card_free(card);
+ if (error)
return error;
- }
error = snd_wss_create(card, miro->wss_base + 4, -1,
- miro->irq, miro->dma1, miro->dma2,
- WSS_HW_AD1845, 0, &codec);
- if (error < 0) {
- snd_card_free(card);
+ miro->irq, miro->dma1, miro->dma2,
+ WSS_HW_DETECT, 0, &codec);
+ if (error < 0)
return error;
- }
error = snd_wss_pcm(codec, 0, &pcm);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
+
error = snd_wss_mixer(codec);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
+
error = snd_wss_timer(codec, 0, &timer);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
miro->pcm = pcm;
- if ((error = snd_miro_mixer(miro)) < 0) {
- snd_card_free(card);
+ error = snd_miro_mixer(card, miro);
+ if (error < 0)
return error;
- }
- if (miro->aci_vendor == 'm') {
+ if (miro->aci->aci_vendor == 'm') {
/* It looks like a miro sound card. */
- switch (miro->aci_product) {
+ switch (miro->aci->aci_product) {
case 'A':
sprintf(card->shortname,
"miroSOUND PCM1 pro / PCM12");
@@ -1380,30 +1374,131 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
card->shortname, miro->name, pcm->name, miro->wss_base + 4,
miro->irq, miro->dma1, miro->dma2);
- if (miro->mpu_port <= 0 || miro->mpu_port == SNDRV_AUTO_PORT)
+ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
- else
- if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- miro->mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
- &rmidi)))
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
+ else {
+ error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
+ &rmidi);
+ if (error < 0)
+ snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
+ }
- if (miro->fm_port > 0 && miro->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3 = NULL;
struct snd_opl4 *opl4;
- if (snd_opl4_create(card, miro->fm_port, miro->fm_port - 8,
+
+ if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0)
- snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", miro->fm_port);
+ snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
+ fm_port);
}
- if ((error = snd_set_aci_init_values(miro)) < 0) {
- snd_card_free(card);
+ error = snd_set_aci_init_values(miro);
+ if (error < 0)
return error;
+
+ return snd_card_register(card);
+}
+
+static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
+{
+#ifdef CONFIG_PNP
+ if (snd_miro_pnp_is_probed)
+ return 0;
+ if (isapnp)
+ return 0;
+#endif
+ return 1;
+}
+
+static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
+{
+ static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+ static int possible_irqs[] = {11, 9, 10, 7, -1};
+ static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+ static int possible_dma1s[] = {3, 1, 0, -1};
+ static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
+ {0, -1} };
+
+ int error;
+ struct snd_miro *miro;
+ struct snd_card *card;
+
+ error = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
+ if (error < 0)
+ return error;
+
+ card->private_free = snd_card_miro_free;
+ miro = card->private_data;
+
+ error = snd_card_miro_detect(card, miro);
+ if (error < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+ return -ENODEV;
+ }
+
+ if (port == SNDRV_AUTO_PORT) {
+ port = snd_legacy_find_free_ioport(possible_ports, 4);
+ if (port < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ return -EBUSY;
+ }
+ }
+
+ if (mpu_port == SNDRV_AUTO_PORT) {
+ mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+ if (mpu_port < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR
+ "unable to find a free MPU401 port\n");
+ return -EBUSY;
+ }
+ }
+
+ if (irq == SNDRV_AUTO_IRQ) {
+ irq = snd_legacy_find_free_irq(possible_irqs);
+ if (irq < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (mpu_irq == SNDRV_AUTO_IRQ) {
+ mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+ if (mpu_irq < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR
+ "unable to find a free MPU401 IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (dma1 == SNDRV_AUTO_DMA) {
+ dma1 = snd_legacy_find_free_dma(possible_dma1s);
+ if (dma1 < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ return -EBUSY;
+ }
+ }
+ if (dma2 == SNDRV_AUTO_DMA) {
+ dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+ if (dma2 < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ return -EBUSY;
+ }
}
snd_card_set_dev(card, devptr);
- if ((error = snd_card_register(card))) {
+ error = snd_miro_probe(card);
+ if (error < 0) {
snd_card_free(card);
return error;
}
@@ -1412,7 +1507,8 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)
return 0;
}
-static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
+static int __devexit snd_miro_isa_remove(struct device *devptr,
+ unsigned int dev)
{
snd_card_free(dev_get_drvdata(devptr));
dev_set_drvdata(devptr, NULL);
@@ -1422,23 +1518,164 @@ static int __devexit snd_miro_remove(struct device *devptr, unsigned int dev)
#define DEV_NAME "miro"
static struct isa_driver snd_miro_driver = {
- .match = snd_miro_match,
- .probe = snd_miro_probe,
- .remove = __devexit_p(snd_miro_remove),
+ .match = snd_miro_isa_match,
+ .probe = snd_miro_isa_probe,
+ .remove = __devexit_p(snd_miro_isa_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
},
};
+#ifdef CONFIG_PNP
+
+static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
+{
+ struct pnp_dev *pdev;
+ int err;
+ struct pnp_dev *devmpu;
+ struct pnp_dev *devmc;
+
+ pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+ if (pdev == NULL)
+ return -EBUSY;
+
+ devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+ if (devmpu == NULL)
+ return -EBUSY;
+
+ devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
+ if (devmc == NULL)
+ return -EBUSY;
+
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ return err;
+ }
+
+ err = pnp_activate_dev(devmc);
+ if (err < 0) {
+ snd_printk(KERN_ERR "OPL syntg pnp configure failure: %d\n",
+ err);
+ return err;
+ }
+
+ port = pnp_port_start(pdev, 1);
+ fm_port = pnp_port_start(pdev, 2) + 8;
+
+ /*
+ * The MC(0) is never accessed and the miroSOUND PCM20 card does not
+ * include it in the PnP resource range. OPTI93x include it.
+ */
+ chip->mc_base = pnp_port_start(devmc, 0) - 1;
+ chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
+
+ irq = pnp_irq(pdev, 0);
+ dma1 = pnp_dma(pdev, 0);
+ dma2 = pnp_dma(pdev, 1);
+
+ if (mpu_port > 0) {
+ err = pnp_activate_dev(devmpu);
+ if (err < 0) {
+ snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ mpu_port = -1;
+ return err;
+ }
+ mpu_port = pnp_port_start(devmpu, 0);
+ mpu_irq = pnp_irq(devmpu, 0);
+ }
+ return 0;
+}
+
+static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ struct snd_card *card;
+ int err;
+ struct snd_miro *miro;
+
+ if (snd_miro_pnp_is_probed)
+ return -EBUSY;
+ if (!isapnp)
+ return -ENODEV;
+ err = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
+ if (err < 0)
+ return err;
+
+ card->private_free = snd_card_miro_free;
+ miro = card->private_data;
+
+ err = snd_card_miro_pnp(miro, pcard, pid);
+ if (err) {
+ snd_card_free(card);
+ return err;
+ }
+
+ /* only miroSOUND PCM20 and PCM12 == OPTi924 */
+ err = snd_miro_init(miro, OPTi9XX_HW_82C924);
+ if (err) {
+ snd_card_free(card);
+ return err;
+ }
+
+ err = snd_miro_opti_check(miro);
+ if (err) {
+ snd_printk(KERN_ERR "OPTI chip not found\n");
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_card_set_dev(card, &pcard->card->dev);
+ err = snd_miro_probe(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pnp_set_card_drvdata(pcard, card);
+ snd_miro_pnp_is_probed = 1;
+ return 0;
+}
+
+static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
+{
+ snd_card_free(pnp_get_card_drvdata(pcard));
+ pnp_set_card_drvdata(pcard, NULL);
+ snd_miro_pnp_is_probed = 0;
+}
+
+static struct pnp_card_driver miro_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DISABLE,
+ .name = "miro",
+ .id_table = snd_miro_pnpids,
+ .probe = snd_miro_pnp_probe,
+ .remove = __devexit_p(snd_miro_pnp_remove),
+};
+#endif
+
static int __init alsa_card_miro_init(void)
{
+#ifdef CONFIG_PNP
+ pnp_register_card_driver(&miro_pnpc_driver);
+ if (snd_miro_pnp_is_probed)
+ return 0;
+ pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
return isa_register_driver(&snd_miro_driver, 1);
}
static void __exit alsa_card_miro_exit(void)
{
- isa_unregister_driver(&snd_miro_driver);
+ if (!snd_miro_pnp_is_probed) {
+ isa_unregister_driver(&snd_miro_driver);
+ return;
+ }
+#ifdef CONFIG_PNP
+ pnp_unregister_card_driver(&miro_pnpc_driver);
+#endif
}
module_init(alsa_card_miro_init)
diff --git a/sound/isa/opti9xx/miro.h b/sound/isa/opti9xx/miro.h
deleted file mode 100644
index 6e1385b8e07..00000000000
--- a/sound/isa/opti9xx/miro.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#ifndef _MIRO_H_
-#define _MIRO_H_
-
-#define ACI_REG_COMMAND 0 /* write register offset */
-#define ACI_REG_STATUS 1 /* read register offset */
-#define ACI_REG_BUSY 2 /* busy register offset */
-#define ACI_REG_RDS 2 /* PCM20: RDS register offset */
-#define ACI_MINTIME 500 /* ACI time out limit */
-
-#define ACI_SET_MUTE 0x0d
-#define ACI_SET_POWERAMP 0x0f
-#define ACI_SET_TUNERMUTE 0xa3
-#define ACI_SET_TUNERMONO 0xa4
-#define ACI_SET_IDE 0xd0
-#define ACI_SET_WSS 0xd1
-#define ACI_SET_SOLOMODE 0xd2
-#define ACI_SET_PREAMP 0x03
-#define ACI_GET_PREAMP 0x21
-#define ACI_WRITE_TUNE 0xa7
-#define ACI_READ_TUNERSTEREO 0xa8
-#define ACI_READ_TUNERSTATION 0xa9
-#define ACI_READ_VERSION 0xf1
-#define ACI_READ_IDCODE 0xf2
-#define ACI_INIT 0xff
-#define ACI_STATUS 0xf0
-#define ACI_S_GENERAL 0x00
-#define ACI_ERROR_OP 0xdf
-
-/* ACI Mixer */
-
-/* These are the values for the right channel GET registers.
- Add an offset of 0x01 for the left channel register.
- (left=right+0x01) */
-
-#define ACI_GET_MASTER 0x03
-#define ACI_GET_MIC 0x05
-#define ACI_GET_LINE 0x07
-#define ACI_GET_CD 0x09
-#define ACI_GET_SYNTH 0x0b
-#define ACI_GET_PCM 0x0d
-#define ACI_GET_LINE1 0x10 /* Radio on PCM20 */
-#define ACI_GET_LINE2 0x12
-
-#define ACI_GET_EQ1 0x22 /* from Bass ... */
-#define ACI_GET_EQ2 0x24
-#define ACI_GET_EQ3 0x26
-#define ACI_GET_EQ4 0x28
-#define ACI_GET_EQ5 0x2a
-#define ACI_GET_EQ6 0x2c
-#define ACI_GET_EQ7 0x2e /* ... to Treble */
-
-/* And these are the values for the right channel SET registers.
- For left channel access you have to add an offset of 0x08.
- MASTER is an exception, which needs an offset of 0x01 */
-
-#define ACI_SET_MASTER 0x00
-#define ACI_SET_MIC 0x30
-#define ACI_SET_LINE 0x31
-#define ACI_SET_CD 0x34
-#define ACI_SET_SYNTH 0x33
-#define ACI_SET_PCM 0x32
-#define ACI_SET_LINE1 0x35 /* Radio on PCM20 */
-#define ACI_SET_LINE2 0x36
-
-#define ACI_SET_EQ1 0x40 /* from Bass ... */
-#define ACI_SET_EQ2 0x41
-#define ACI_SET_EQ3 0x42
-#define ACI_SET_EQ4 0x43
-#define ACI_SET_EQ5 0x44
-#define ACI_SET_EQ6 0x45
-#define ACI_SET_EQ7 0x46 /* ... to Treble */
-
-#endif /* _MIRO_H_ */
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 5cd555325b9..c8a8da0d403 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -135,21 +135,15 @@ struct snd_opti9xx {
unsigned long mc_base_size;
#ifdef OPTi93X
unsigned long mc_indir_index;
+ unsigned long mc_indir_size;
+ struct resource *res_mc_indir;
struct snd_wss *codec;
#endif /* OPTi93X */
unsigned long pwd_reg;
spinlock_t lock;
- long wss_base;
int irq;
- int dma1;
- int dma2;
-
- long fm_port;
-
- long mpu_port;
- int mpu_irq;
#ifdef CONFIG_PNP
struct pnp_dev *dev;
@@ -185,7 +179,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
#endif
static char * snd_opti9xx_names[] = {
- "unkown",
+ "unknown",
"82C928", "82C929",
"82C924", "82C925",
"82C930", "82C931", "82C933"
@@ -216,13 +210,7 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
spin_lock_init(&chip->lock);
- chip->wss_base = -1;
chip->irq = -1;
- chip->dma1 = -1;
- chip->dma2 = -1;
- chip->fm_port = -1;
- chip->mpu_port = -1;
- chip->mpu_irq = -1;
switch (hardware) {
#ifndef OPTi93X
@@ -245,7 +233,10 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
case OPTi9XX_HW_82C931:
case OPTi9XX_HW_82C933:
chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
- chip->mc_indir_index = 0xe0e;
+ if (!chip->mc_indir_index) {
+ chip->mc_indir_index = 0xe0e;
+ chip->mc_indir_size = 2;
+ }
chip->password = 0xe4;
chip->pwd_reg = 0;
break;
@@ -348,7 +339,10 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
-static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
+static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
+ long wss_base,
+ int irq, int dma1, int dma2,
+ long mpu_port, int mpu_irq)
{
unsigned char wss_base_bits;
unsigned char irq_bits;
@@ -416,7 +410,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
return -EINVAL;
}
- switch (chip->wss_base) {
+ switch (wss_base) {
case 0x530:
wss_base_bits = 0x00;
break;
@@ -430,14 +424,13 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n",
- chip->wss_base);
+ snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", wss_base);
goto __skip_base;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
__skip_base:
- switch (chip->irq) {
+ switch (irq) {
//#ifdef OPTi93X
case 5:
irq_bits = 0x05;
@@ -456,11 +449,11 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq);
+ snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
goto __skip_resources;
}
- switch (chip->dma1) {
+ switch (dma1) {
case 0:
dma_bits = 0x01;
break;
@@ -471,38 +464,36 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n",
- chip->dma1);
+ snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
goto __skip_resources;
}
#if defined(CS4231) || defined(OPTi93X)
- if (chip->dma1 == chip->dma2) {
+ if (dma1 == dma2) {
snd_printk(KERN_ERR "don't want to share dmas\n");
return -EBUSY;
}
- switch (chip->dma2) {
+ switch (dma2) {
case 0:
case 1:
break;
default:
- snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n",
- chip->dma2);
+ snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
#endif /* CS4231 || OPTi93X */
#ifndef OPTi93X
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ outb(irq_bits << 3 | dma_bits, wss_base);
#else /* OPTi93X */
snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
#endif /* OPTi93X */
__skip_resources:
if (chip->hardware > OPTi9XX_HW_82C928) {
- switch (chip->mpu_port) {
+ switch (mpu_port) {
case 0:
case -1:
break;
@@ -520,12 +511,11 @@ __skip_resources:
break;
default:
snd_printk(KERN_WARNING
- "MPU-401 port 0x%lx not valid\n",
- chip->mpu_port);
+ "MPU-401 port 0x%lx not valid\n", mpu_port);
goto __skip_mpu;
}
- switch (chip->mpu_irq) {
+ switch (mpu_irq) {
case 5:
mpu_irq_bits = 0x02;
break;
@@ -540,12 +530,12 @@ __skip_resources:
break;
default:
snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
- chip->mpu_irq);
+ mpu_irq);
goto __skip_mpu;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
- (chip->mpu_port <= 0) ? 0x00 :
+ (mpu_port <= 0) ? 0x00 :
0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
0xf8);
}
@@ -558,10 +548,13 @@ __skip_mpu:
static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
{
- struct snd_wss *codec = dev_id;
- struct snd_opti9xx *chip = codec->card->private_data;
+ struct snd_opti9xx *chip = dev_id;
+ struct snd_wss *codec = chip->codec;
unsigned char status;
+ if (!codec)
+ return IRQ_HANDLED;
+
status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
snd_pcm_period_elapsed(codec->playback_substream);
@@ -575,57 +568,69 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
#endif /* OPTi93X */
-static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
- struct snd_opti9xx *chip)
+static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
{
- int i, err;
+ unsigned char value;
+#ifdef OPTi93X
+ unsigned long flags;
+#endif
+ chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
+ "OPTi9xx MC");
+ if (chip->res_mc_base == NULL)
+ return -EBUSY;
#ifndef OPTi93X
- for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
- unsigned char value;
+ value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
+ if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
+ if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
+ return 0;
+#else /* OPTi93X */
+ chip->res_mc_indir = request_region(chip->mc_indir_index,
+ chip->mc_indir_size,
+ "OPTi93x MC");
+ if (chip->res_mc_indir == NULL)
+ return -EBUSY;
- if ((err = snd_opti9xx_init(chip, i)) < 0)
- return err;
+ spin_lock_irqsave(&chip->lock, flags);
+ outb(chip->password, chip->mc_base + chip->pwd_reg);
+ outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
+ spin_unlock_irqrestore(&chip->lock, flags);
- if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
- continue;
+ value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
+ snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
+ if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+ return 0;
- value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
- if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
- if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
- return 1;
+ release_and_free_resource(chip->res_mc_indir);
+ chip->res_mc_indir = NULL;
+#endif /* OPTi93X */
+ release_and_free_resource(chip->res_mc_base);
+ chip->res_mc_base = NULL;
- release_and_free_resource(chip->res_mc_base);
- chip->res_mc_base = NULL;
+ return -ENODEV;
+}
- }
-#else /* OPTi93X */
- for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
- unsigned long flags;
- unsigned char value;
+static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
+ struct snd_opti9xx *chip)
+{
+ int i, err;
- if ((err = snd_opti9xx_init(chip, i)) < 0)
+#ifndef OPTi93X
+ for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
+#else
+ for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
+#endif
+ err = snd_opti9xx_init(chip, i);
+ if (err < 0)
return err;
- if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
- continue;
-
- spin_lock_irqsave(&chip->lock, flags);
- outb(chip->password, chip->mc_base + chip->pwd_reg);
- outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
- ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
- spin_unlock_irqrestore(&chip->lock, flags);
-
- value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
- snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
- if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+ err = snd_opti9xx_read_check(chip);
+ if (err == 0)
return 1;
-
- release_and_free_resource(chip->res_mc_base);
- chip->res_mc_base = NULL;
+#ifdef OPTi93X
+ chip->mc_indir_index = 0;
+#endif
}
-#endif /* OPTi93X */
-
return -ENODEV;
}
@@ -654,6 +659,8 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
#ifdef OPTi93X
port = pnp_port_start(pdev, 0) - 4;
fm_port = pnp_port_start(pdev, 1) + 8;
+ chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;
+ chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;
#else
if (pid->driver_data != 0x0924)
port = pnp_port_start(pdev, 1);
@@ -684,14 +691,14 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
static void snd_card_opti9xx_free(struct snd_card *card)
{
struct snd_opti9xx *chip = card->private_data;
-
+
if (chip) {
#ifdef OPTi93X
- struct snd_wss *codec = chip->codec;
- if (codec && codec->irq > 0) {
- disable_irq(codec->irq);
- free_irq(codec->irq, codec);
+ if (chip->irq > 0) {
+ disable_irq(chip->irq);
+ free_irq(chip->irq, chip);
}
+ release_and_free_resource(chip->res_mc_indir);
#endif
release_and_free_resource(chip->res_mc_base);
}
@@ -701,6 +708,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
{
static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
int error;
+ int xdma2;
struct snd_opti9xx *chip = card->private_data;
struct snd_wss *codec;
#ifdef CS4231
@@ -710,36 +718,25 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
struct snd_rawmidi *rmidi;
struct snd_hwdep *synth;
- if (! chip->res_mc_base &&
- (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
- "OPTi9xx MC")) == NULL)
- return -ENOMEM;
-
- chip->wss_base = port;
- chip->fm_port = fm_port;
- chip->mpu_port = mpu_port;
- chip->irq = irq;
- chip->mpu_irq = mpu_irq;
- chip->dma1 = dma1;
#if defined(CS4231) || defined(OPTi93X)
- chip->dma2 = dma2;
+ xdma2 = dma2;
#else
- chip->dma2 = -1;
+ xdma2 = -1;
#endif
- if (chip->wss_base == SNDRV_AUTO_PORT) {
- chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
- if (chip->wss_base < 0) {
+ if (port == SNDRV_AUTO_PORT) {
+ port = snd_legacy_find_free_ioport(possible_ports, 4);
+ if (port < 0) {
snd_printk(KERN_ERR "unable to find a free WSS port\n");
return -EBUSY;
}
}
- error = snd_opti9xx_configure(chip);
+ error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
+ mpu_port, mpu_irq);
if (error)
return error;
- error = snd_wss_create(card, chip->wss_base + 4, -1,
- chip->irq, chip->dma1, chip->dma2,
+ error = snd_wss_create(card, port + 4, -1, irq, dma1, xdma2,
#ifdef OPTi93X
WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
#else
@@ -763,35 +760,35 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return error;
#endif
#ifdef OPTi93X
- error = request_irq(chip->irq, snd_opti93x_interrupt,
- IRQF_DISABLED, DEV_NAME" - WSS", codec);
+ error = request_irq(irq, snd_opti93x_interrupt,
+ IRQF_DISABLED, DEV_NAME" - WSS", chip);
if (error < 0) {
- snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
+ snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
return error;
}
#endif
+ chip->irq = irq;
strcpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, pcm->name, chip->wss_base + 4,
- chip->irq, chip->dma1, chip->dma2);
+ card->shortname, pcm->name, port + 4, irq, dma1, xdma2);
#else
sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, pcm->name, chip->wss_base + 4,
- chip->irq, chip->dma1);
+ card->shortname, pcm->name, port + 4, irq, dma1);
#endif /* CS4231 || OPTi93X */
- if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
+ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
- else
- if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
- &rmidi)))
+ else {
+ error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
+ if (error)
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- chip->mpu_port);
+ mpu_port);
+ }
- if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
+ if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3 = NULL;
#ifndef OPTi93X
if (chip->hardware == OPTi9XX_HW_82C928 ||
@@ -801,9 +798,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
/* assume we have an OPL4 */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
0x20, 0x20);
- if (snd_opl4_create(card,
- chip->fm_port,
- chip->fm_port - 8,
+ if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0) {
/* no luck, use OPL3 instead */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
@@ -811,12 +806,10 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
}
}
#endif /* !OPTi93X */
- if (!opl3 && snd_opl3_create(card,
- chip->fm_port,
- chip->fm_port + 2,
+ if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- chip->fm_port, chip->fm_port + 4 - 1);
+ fm_port, fm_port + 4 - 1);
}
if (opl3) {
error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
@@ -978,6 +971,13 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
}
if (hw <= OPTi9XX_HW_82C930)
chip->mc_base -= 0x80;
+
+ error = snd_opti9xx_read_check(chip);
+ if (error) {
+ snd_printk(KERN_ERR "OPTI chip not found\n");
+ snd_card_free(card);
+ return error;
+ }
snd_card_set_dev(card, &pcard->card->dev);
if ((error = snd_opti9xx_probe(card)) < 0) {
snd_card_free(card);
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index 96678d5d383..751762f1c59 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -393,8 +393,6 @@ size_dram(struct snd_emu8000 *emu)
while (size < EMU8000_MAX_DRAM) {
- size += 512 * 1024; /* increment 512kbytes */
-
/* Write a unique data on the test address.
* if the address is out of range, the data is written on
* 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is
@@ -414,7 +412,9 @@ size_dram(struct snd_emu8000 *emu)
/*snd_emu8000_read_wait(emu);*/
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
- break; /* we must have wrapped around */
+ break; /* no memory at this address */
+
+ size += 512 * 1024; /* increment 512kbytes */
snd_emu8000_read_wait(emu);
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 475220bbcc9..318ff0c823e 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -631,7 +631,7 @@ static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
- SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
+ SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
static struct sbmix_elem snd_sb16_ctl_capture_vol =
SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
static struct sbmix_elem snd_sb16_ctl_play_vol =
@@ -689,7 +689,7 @@ static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
- SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7);
+ SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0, 7);
static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 66187122377..e2d5d2d3ed9 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,5 +1,5 @@
/*
- * Low-level ALSA driver for the ENSONIQ SoundScape PnP
+ * Low-level ALSA driver for the ENSONIQ SoundScape
* Copyright (c) by Chris Rankin
*
* This driver was written in part using information obtained from
@@ -25,31 +25,36 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/pnp.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <asm/dma.h>
#include <sound/core.h>
-#include <sound/hwdep.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
-#include <sound/sscape_ioctl.h>
-
MODULE_AUTHOR("Chris Rankin");
-MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");
+MODULE_DESCRIPTION("ENSONIQ SoundScape driver");
MODULE_LICENSE("GPL");
-
-static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;
-static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;
-static long port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static long wss_port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;
-static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
-static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
-static int dma2[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
+MODULE_FIRMWARE("sndscape.co0");
+MODULE_FIRMWARE("sndscape.co1");
+MODULE_FIRMWARE("sndscape.co2");
+MODULE_FIRMWARE("sndscape.co3");
+MODULE_FIRMWARE("sndscape.co4");
+MODULE_FIRMWARE("scope.cod");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static bool joystick[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
@@ -75,6 +80,9 @@ MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
module_param_array(dma2, int, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable gameport.");
+
#ifdef CONFIG_PNP
static int isa_registered;
static int pnp_registered;
@@ -101,14 +109,14 @@ MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
#define RX_READY 0x01
#define TX_READY 0x02
-#define CMD_ACK 0x80
-#define CMD_SET_MIDI_VOL 0x84
-#define CMD_GET_MIDI_VOL 0x85
-#define CMD_XXX_MIDI_VOL 0x86
-#define CMD_SET_EXTMIDI 0x8a
-#define CMD_GET_EXTMIDI 0x8b
-#define CMD_SET_MT32 0x8c
-#define CMD_GET_MT32 0x8d
+#define CMD_ACK 0x80
+#define CMD_SET_MIDI_VOL 0x84
+#define CMD_GET_MIDI_VOL 0x85
+#define CMD_XXX_MIDI_VOL 0x86
+#define CMD_SET_EXTMIDI 0x8a
+#define CMD_GET_EXTMIDI 0x8b
+#define CMD_SET_MT32 0x8c
+#define CMD_GET_MT32 0x8d
enum GA_REG {
GA_INTSTAT_REG = 0,
@@ -127,7 +135,8 @@ enum GA_REG {
enum card_type {
- SSCAPE,
+ MEDIA_FX, /* Sequoia S-1000 */
+ SSCAPE, /* Sequoia S-2000 */
SSCAPE_PNP,
SSCAPE_VIVO,
};
@@ -140,16 +149,7 @@ struct soundscape {
struct resource *io_res;
struct resource *wss_res;
struct snd_wss *chip;
- struct snd_mpu401 *mpu;
- struct snd_hwdep *hw;
- /*
- * The MIDI device won't work until we've loaded
- * its firmware via a hardware-dependent device IOCTL
- */
- spinlock_t fwlock;
- int hw_in_use;
- unsigned long midi_usage;
unsigned char midi_vol;
};
@@ -161,28 +161,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
return (struct soundscape *) (c->private_data);
}
-static inline struct soundscape *get_mpu401_soundscape(struct snd_mpu401 * mpu)
-{
- return (struct soundscape *) (mpu->private_data);
-}
-
-static inline struct soundscape *get_hwdep_soundscape(struct snd_hwdep * hw)
-{
- return (struct soundscape *) (hw->private_data);
-}
-
-
/*
* Allocates some kernel memory that we can use for DMA.
* I think this means that the memory has to map to
* contiguous pages of physical memory.
*/
-static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size)
+static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
+ unsigned long size)
{
if (buf) {
- if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),
+ if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(),
size, buf) < 0) {
- snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size);
+ snd_printk(KERN_ERR "sscape: Failed to allocate "
+ "%lu bytes for DMA\n",
+ size);
return NULL;
}
}
@@ -199,13 +192,13 @@ static void free_dmabuf(struct snd_dma_buffer *buf)
snd_dma_free_pages(buf);
}
-
/*
* This function writes to the SoundScape's control registers,
* but doesn't do any locking. It's up to the caller to do that.
* This is why this function is "unsafe" ...
*/
-static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val)
+static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
+ unsigned char val)
{
outb(reg, ODIE_ADDR_IO(io_base));
outb(val, ODIE_DATA_IO(io_base));
@@ -215,7 +208,8 @@ static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsign
* Write to the SoundScape's control registers, and do the
* necessary locking ...
*/
-static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val)
+static void sscape_write(struct soundscape *s, enum GA_REG reg,
+ unsigned char val)
{
unsigned long flags;
@@ -228,7 +222,8 @@ static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char va
* Read from the SoundScape's control registers, but leave any
* locking to the caller. This is why the function is "unsafe" ...
*/
-static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg)
+static inline unsigned char sscape_read_unsafe(unsigned io_base,
+ enum GA_REG reg)
{
outb(reg, ODIE_ADDR_IO(io_base));
return inb(ODIE_DATA_IO(io_base));
@@ -257,9 +252,8 @@ static inline void set_midi_mode_unsafe(unsigned io_base)
static inline int host_read_unsafe(unsigned io_base)
{
int data = -1;
- if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) {
+ if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0)
data = inb(HOST_DATA_IO(io_base));
- }
return data;
}
@@ -301,7 +295,7 @@ static inline int host_write_unsafe(unsigned io_base, unsigned char data)
* Also leaves all locking-issues to the caller ...
*/
static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
- unsigned timeout)
+ unsigned timeout)
{
int err;
@@ -320,7 +314,7 @@ static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
*
* NOTE: This check is based upon observation, not documentation.
*/
-static inline int verify_mpu401(const struct snd_mpu401 * mpu)
+static inline int verify_mpu401(const struct snd_mpu401 *mpu)
{
return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
}
@@ -328,7 +322,7 @@ static inline int verify_mpu401(const struct snd_mpu401 * mpu)
/*
* This is apparently the standard way to initailise an MPU-401
*/
-static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
+static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
{
outb(0, MPU401D(mpu));
}
@@ -338,9 +332,10 @@ static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
* The AD1845 detection fails if we *don't* do this, so I
* think that this is a good idea ...
*/
-static inline void activate_ad1845_unsafe(unsigned io_base)
+static void activate_ad1845_unsafe(unsigned io_base)
{
- sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10);
+ unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10);
sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
}
@@ -359,24 +354,27 @@ static void soundscape_free(struct snd_card *c)
* Tell the SoundScape to begin a DMA tranfer using the given channel.
* All locking issues are left to the caller.
*/
-static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
+static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
{
- sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01);
- sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);
+ sscape_write_unsafe(io_base, reg,
+ sscape_read_unsafe(io_base, reg) | 0x01);
+ sscape_write_unsafe(io_base, reg,
+ sscape_read_unsafe(io_base, reg) & 0xfe);
}
/*
* Wait for a DMA transfer to complete. This is a "limited busy-wait",
* and all locking issues are left to the caller.
*/
-static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout)
+static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg,
+ unsigned timeout)
{
while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
udelay(100);
--timeout;
} /* while */
- return (sscape_read_unsafe(io_base, reg) & 0x01);
+ return sscape_read_unsafe(io_base, reg) & 0x01;
}
/*
@@ -392,12 +390,12 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
- if ((x & 0xfe) == 0xfe)
+ if (x == 0xfe || x == 0xff)
return 1;
msleep(10);
@@ -419,10 +417,10 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
if (x == 0xfe)
return 1;
@@ -436,15 +434,15 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
/*
* Upload a byte-stream into the SoundScape using DMA channel A.
*/
-static int upload_dma_data(struct soundscape *s,
- const unsigned char __user *data,
- size_t size)
+static int upload_dma_data(struct soundscape *s, const unsigned char *data,
+ size_t size)
{
unsigned long flags;
struct snd_dma_buffer dma;
int ret;
+ unsigned char val;
- if (!get_dmabuf(&dma, PAGE_ALIGN(size)))
+ if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
spin_lock_irqsave(&s->lock, flags);
@@ -452,70 +450,57 @@ static int upload_dma_data(struct soundscape *s,
/*
* Reset the board ...
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
/*
* Enable the DMA channels and configure them ...
*/
- sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);
- sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);
+ val = (s->chip->dma1 << 4) | DMA_8BIT;
+ sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
/*
* Take the board out of reset ...
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
/*
- * Upload the user's data (firmware?) to the SoundScape
+ * Upload the firmware to the SoundScape
* board through the DMA channel ...
*/
while (size != 0) {
unsigned long len;
- /*
- * Apparently, copying to/from userspace can sleep.
- * We are therefore forbidden from holding any
- * spinlocks while we copy ...
- */
- spin_unlock_irqrestore(&s->lock, flags);
-
- /*
- * Remember that the data that we want to DMA
- * comes from USERSPACE. We have already verified
- * the userspace pointer ...
- */
len = min(size, dma.bytes);
- len -= __copy_from_user(dma.area, data, len);
+ memcpy(dma.area, data, len);
data += len;
size -= len;
- /*
- * Grab that spinlock again, now that we've
- * finished copying!
- */
- spin_lock_irqsave(&s->lock, flags);
-
snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
/*
- * Don't forget to release this spinlock we're holding ...
+ * Don't forget to release this spinlock we're holding
*/
spin_unlock_irqrestore(&s->lock, flags);
- snd_printk(KERN_ERR "sscape: DMA upload has timed out\n");
+ snd_printk(KERN_ERR
+ "sscape: DMA upload has timed out\n");
ret = -EAGAIN;
goto _release_dma;
}
} /* while */
set_host_mode_unsafe(s->io_base);
+ outb(0x0, s->io_base);
/*
* Boot the board ... (I think)
*/
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x40);
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
spin_unlock_irqrestore(&s->lock, flags);
/*
@@ -525,10 +510,12 @@ static int upload_dma_data(struct soundscape *s,
*/
ret = 0;
if (!obp_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n");
+ snd_printk(KERN_ERR "sscape: No response "
+ "from on-board processor after upload\n");
ret = -EAGAIN;
} else if (!host_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n");
+ snd_printk(KERN_ERR
+ "sscape: SoundScape failed to initialise\n");
ret = -EAGAIN;
}
@@ -536,7 +523,7 @@ _release_dma:
/*
* NOTE!!! We are NOT holding any spinlocks at this point !!!
*/
- sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40));
+ sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
free_dmabuf(&dma);
return ret;
@@ -546,167 +533,76 @@ _release_dma:
* Upload the bootblock(?) into the SoundScape. The only
* purpose of this block of code seems to be to tell
* us which version of the microcode we should be using.
- *
- * NOTE: The boot-block data resides in USER-SPACE!!!
- * However, we have already verified its memory
- * addresses by the time we get here.
*/
-static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock __user *bb)
+static int sscape_upload_bootblock(struct snd_card *card)
{
+ struct soundscape *sscape = get_card_soundscape(card);
unsigned long flags;
+ const struct firmware *init_fw = NULL;
int data = 0;
int ret;
- ret = upload_dma_data(sscape, bb->code, sizeof(bb->code));
-
- spin_lock_irqsave(&sscape->lock, flags);
- if (ret == 0) {
- data = host_read_ctrl_unsafe(sscape->io_base, 100);
- }
- set_midi_mode_unsafe(sscape->io_base);
- spin_unlock_irqrestore(&sscape->lock, flags);
-
- if (ret == 0) {
- if (data < 0) {
- snd_printk(KERN_ERR "sscape: timeout reading firmware version\n");
- ret = -EAGAIN;
- }
- else if (__copy_to_user(&bb->version, &data, sizeof(bb->version))) {
- ret = -EFAULT;
- }
+ ret = request_firmware(&init_fw, "scope.cod", card->dev);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Error loading scope.cod");
+ return ret;
}
+ ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
- return ret;
-}
-
-/*
- * Upload the microcode into the SoundScape. The
- * microcode is 64K of data, and if we try to copy
- * it into a local variable then we will SMASH THE
- * KERNEL'S STACK! We therefore leave it in USER
- * SPACE, and save ourselves from copying it at all.
- */
-static int sscape_upload_microcode(struct soundscape *sscape,
- const struct sscape_microcode __user *mc)
-{
- unsigned long flags;
- char __user *code;
- int err;
+ release_firmware(init_fw);
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now.
- *
- * NOTE: This buffer is 64K long! That's WAY too big to
- * copy into a stack-temporary anyway.
- */
- if ( get_user(code, &mc->code) ||
- !access_ok(VERIFY_READ, code, SSCAPE_MICROCODE_SIZE) )
- return -EFAULT;
+ spin_lock_irqsave(&sscape->lock, flags);
+ if (ret == 0)
+ data = host_read_ctrl_unsafe(sscape->io_base, 100);
- if ((err = upload_dma_data(sscape, code, SSCAPE_MICROCODE_SIZE)) == 0) {
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n");
- }
+ if (data & 0x10)
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
- spin_lock_irqsave(&sscape->lock, flags);
- set_midi_mode_unsafe(sscape->io_base);
spin_unlock_irqrestore(&sscape->lock, flags);
- initialise_mpu401(sscape->mpu);
+ data &= 0xf;
+ if (ret == 0 && data > 7) {
+ snd_printk(KERN_ERR
+ "sscape: timeout reading firmware version\n");
+ ret = -EAGAIN;
+ }
- return err;
+ return (ret == 0) ? data : ret;
}
/*
- * Hardware-specific device functions, to implement special
- * IOCTLs for the SoundScape card. This is how we upload
- * the microcode into the card, for example, and so we
- * must ensure that no two processes can open this device
- * simultaneously, and that we can't open it at all if
- * someone is using the MIDI device.
+ * Upload the microcode into the SoundScape.
*/
-static int sscape_hw_open(struct snd_hwdep * hw, struct file *file)
+static int sscape_upload_microcode(struct snd_card *card, int version)
{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
+ struct soundscape *sscape = get_card_soundscape(card);
+ const struct firmware *init_fw = NULL;
+ char name[14];
int err;
- spin_lock_irqsave(&sscape->fwlock, flags);
+ snprintf(name, sizeof(name), "sndscape.co%d", version);
- if ((sscape->midi_usage != 0) || sscape->hw_in_use) {
- err = -EBUSY;
- } else {
- sscape->hw_in_use = 1;
- err = 0;
+ err = request_firmware(&init_fw, name, card->dev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
+ version);
+ return err;
}
+ err = upload_dma_data(sscape, init_fw->data, init_fw->size);
+ if (err == 0)
+ snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs\n",
+ init_fw->size >> 10);
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return err;
-}
-
-static int sscape_hw_release(struct snd_hwdep * hw, struct file *file)
-{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
- sscape->hw_in_use = 0;
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return 0;
-}
-
-static int sscape_hw_ioctl(struct snd_hwdep * hw, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct soundscape *sscape = get_hwdep_soundscape(hw);
- int err = -EBUSY;
-
- switch (cmd) {
- case SND_SSCAPE_LOAD_BOOTB:
- {
- register struct sscape_bootblock __user *bb = (struct sscape_bootblock __user *) arg;
-
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now ...
- */
- if ( !access_ok(VERIFY_READ, bb->code, sizeof(bb->code)) )
- return -EFAULT;
-
- /*
- * Now check that we can write the firmware version number too...
- */
- if ( !access_ok(VERIFY_WRITE, &bb->version, sizeof(bb->version)) )
- return -EFAULT;
-
- err = sscape_upload_bootblock(sscape, bb);
- }
- break;
-
- case SND_SSCAPE_LOAD_MCODE:
- {
- register const struct sscape_microcode __user *mc = (const struct sscape_microcode __user *) arg;
-
- err = sscape_upload_microcode(sscape, mc);
- }
- break;
-
- default:
- err = -EINVAL;
- break;
- } /* switch */
+ release_firmware(init_fw);
return err;
}
-
/*
* Mixer control for the SoundScape's MIDI device.
*/
static int sscape_midi_info(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -716,7 +612,7 @@ static int sscape_midi_info(struct snd_kcontrol *ctl,
}
static int sscape_midi_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
+ struct snd_ctl_elem_value *uctl)
{
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
@@ -730,16 +626,18 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
}
static int sscape_midi_put(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
+ struct snd_ctl_elem_value *uctl)
{
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
- register struct soundscape *s = get_card_soundscape(card);
+ struct soundscape *s = get_card_soundscape(card);
unsigned long flags;
int change;
+ unsigned char new_val;
spin_lock_irqsave(&s->lock, flags);
+ new_val = uctl->value.integer.value[0] & 127;
/*
* We need to put the board into HOST mode before we
* can send any volume-changing HOST commands ...
@@ -752,15 +650,16 @@ static int sscape_midi_put(struct snd_kcontrol *kctl,
* and then perform another volume-related command. Perhaps the
* first command is an "open" and the second command is a "close"?
*/
- if (s->midi_vol == ((unsigned char) uctl->value.integer. value[0] & 127)) {
+ if (s->midi_vol == new_val) {
change = 0;
goto __skip_change;
}
- change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
- && host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100)
- && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100));
- s->midi_vol = (unsigned char) uctl->value.integer.value[0] & 127;
- __skip_change:
+ change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
+ && host_write_ctrl_unsafe(s->io_base, new_val, 100)
+ && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)
+ && host_write_ctrl_unsafe(s->io_base, new_val, 100);
+ s->midi_vol = new_val;
+__skip_change:
/*
* Take the board out of HOST mode and back into MIDI mode ...
@@ -784,20 +683,25 @@ static struct snd_kcontrol_new midi_mixer_ctl = {
* These IRQs are encoded as bit patterns so that they can be
* written to the control registers.
*/
-static unsigned __devinit get_irq_config(int irq)
+static unsigned __devinit get_irq_config(int sscape_type, int irq)
{
static const int valid_irq[] = { 9, 5, 7, 10 };
+ static const int old_irq[] = { 9, 7, 5, 15 };
unsigned cfg;
- for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) {
- if (irq == valid_irq[cfg])
- return cfg;
- } /* for */
+ if (sscape_type == MEDIA_FX) {
+ for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg)
+ if (irq == old_irq[cfg])
+ return cfg;
+ } else {
+ for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg)
+ if (irq == valid_irq[cfg])
+ return cfg;
+ }
return INVALID_IRQ;
}
-
/*
* Perform certain arcane port-checks to see whether there
* is a SoundScape board lurking behind the given ports.
@@ -842,11 +746,38 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
goto _done;
- d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+ if (s->ic_type == IC_OPUS)
+ activate_ad1845_unsafe(s->io_base);
if (s->type == SSCAPE_VIVO)
wss_io += 4;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
+
+ /* wait for WSS codec */
+ for (d = 0; d < 500; d++) {
+ if ((inb(wss_io) & 0x80) == 0)
+ break;
+ spin_unlock_irqrestore(&s->lock, flags);
+ msleep(1);
+ spin_lock_irqsave(&s->lock, flags);
+ }
+
+ if ((inb(wss_io) & 0x80) != 0)
+ goto _done;
+
+ if (inb(wss_io + 2) == 0xff)
+ goto _done;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d);
+
+ if ((inb(wss_io) & 0x80) != 0)
+ s->type = MEDIA_FX;
+
+ d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
/* wait for WSS codec */
for (d = 0; d < 500; d++) {
if ((inb(wss_io) & 0x80) == 0)
@@ -855,14 +786,13 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
msleep(1);
spin_lock_irqsave(&s->lock, flags);
}
- snd_printd(KERN_INFO "init delay = %d ms\n", d);
/*
* SoundScape successfully detected!
*/
retval = 1;
- _done:
+_done:
spin_unlock_irqrestore(&s->lock, flags);
return retval;
}
@@ -873,63 +803,35 @@ static int __devinit detect_sscape(struct soundscape *s, long wss_io)
* to crash the machine. Also check that someone isn't using the hardware
* IOCTL device.
*/
-static int mpu401_open(struct snd_mpu401 * mpu)
+static int mpu401_open(struct snd_mpu401 *mpu)
{
- int err;
-
if (!verify_mpu401(mpu)) {
- snd_printk(KERN_ERR "sscape: MIDI disabled, please load firmware\n");
- err = -ENODEV;
- } else {
- register struct soundscape *sscape = get_mpu401_soundscape(mpu);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
-
- if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) {
- err = -EBUSY;
- } else {
- ++(sscape->midi_usage);
- err = 0;
- }
-
- spin_unlock_irqrestore(&sscape->fwlock, flags);
+ snd_printk(KERN_ERR "sscape: MIDI disabled, "
+ "please load firmware\n");
+ return -ENODEV;
}
- return err;
-}
-
-static void mpu401_close(struct snd_mpu401 * mpu)
-{
- register struct soundscape *sscape = get_mpu401_soundscape(mpu);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
- --(sscape->midi_usage);
- spin_unlock_irqrestore(&sscape->fwlock, flags);
+ return 0;
}
/*
* Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
*/
-static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned long port, int irq)
+static int __devinit create_mpu401(struct snd_card *card, int devnum,
+ unsigned long port, int irq)
{
struct soundscape *sscape = get_card_soundscape(card);
struct snd_rawmidi *rawmidi;
int err;
- if ((err = snd_mpu401_uart_new(card, devnum,
- MPU401_HW_MPU401,
- port, MPU401_INFO_INTEGRATED,
- irq, IRQF_DISABLED,
- &rawmidi)) == 0) {
- struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data;
+ err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
+ MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
+ &rawmidi);
+ if (err == 0) {
+ struct snd_mpu401 *mpu = rawmidi->private_data;
mpu->open_input = mpu401_open;
mpu->open_output = mpu401_open;
- mpu->close_input = mpu401_close;
- mpu->close_output = mpu401_close;
mpu->private_data = sscape;
- sscape->mpu = mpu;
initialise_mpu401(mpu);
}
@@ -950,32 +852,34 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
register struct soundscape *sscape = get_card_soundscape(card);
struct snd_wss *chip;
int err;
+ int codec_type = WSS_HW_DETECT;
- if (sscape->type == SSCAPE_VIVO)
- port += 4;
+ switch (sscape->type) {
+ case MEDIA_FX:
+ case SSCAPE:
+ /*
+ * There are some freak examples of early Soundscape cards
+ * with CS4231 instead of AD1848/CS4248. Unfortunately, the
+ * CS4231 works only in CS4248 compatibility mode on
+ * these cards so force it.
+ */
+ if (sscape->ic_type != IC_OPUS)
+ codec_type = WSS_HW_AD1848;
+ break;
- if (dma1 == dma2)
- dma2 = -1;
+ case SSCAPE_VIVO:
+ port += 4;
+ break;
+ default:
+ break;
+ }
err = snd_wss_create(card, port, -1, irq, dma1, dma2,
- WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);
+ codec_type, WSS_HWSHARE_DMA1, &chip);
if (!err) {
unsigned long flags;
struct snd_pcm *pcm;
-/*
- * It turns out that the PLAYBACK_ENABLE bit is set
- * by the lowlevel driver ...
- *
-#define AD1845_IFACE_CONFIG \
- (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
- snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_wss_mce_down(chip);
- */
-
if (sscape->type != SSCAPE_VIVO) {
/*
* The input clock frequency on the SoundScape must
@@ -1022,17 +926,10 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
}
}
- strcpy(card->driver, "SoundScape");
- strcpy(card->shortname, pcm->name);
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
- pcm->name, chip->port, chip->irq,
- chip->dma1, chip->dma2);
-
sscape->chip = chip;
}
- _error:
+_error:
return err;
}
@@ -1051,21 +948,8 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
struct resource *wss_res;
unsigned long flags;
int err;
-
- /*
- * Check that the user didn't pass us garbage data ...
- */
- irq_cfg = get_irq_config(irq[dev]);
- if (irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
- return -ENXIO;
- }
-
- mpu_irq_cfg = get_irq_config(mpu_irq[dev]);
- if (mpu_irq_cfg == INVALID_IRQ) {
- printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
- return -ENXIO;
- }
+ int val;
+ const char *name;
/*
* Grab IO ports that we will need to probe so that we
@@ -1098,41 +982,51 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
}
spin_lock_init(&sscape->lock);
- spin_lock_init(&sscape->fwlock);
sscape->io_res = io_res;
sscape->wss_res = wss_res;
sscape->io_base = port[dev];
if (!detect_sscape(sscape, wss_port[dev])) {
- printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base);
+ printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
+ sscape->io_base);
err = -ENODEV;
goto _release_dma;
}
- printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n",
- sscape->io_base, irq[dev], dma[dev]);
+ switch (sscape->type) {
+ case MEDIA_FX:
+ name = "MediaFX/SoundFX";
+ break;
+ case SSCAPE:
+ name = "Soundscape";
+ break;
+ case SSCAPE_PNP:
+ name = "Soundscape PnP";
+ break;
+ case SSCAPE_VIVO:
+ name = "Soundscape VIVO";
+ break;
+ default:
+ name = "unknown Soundscape";
+ break;
+ }
- if (sscape->type != SSCAPE_VIVO) {
- /*
- * Now create the hardware-specific device so that we can
- * load the microcode into the on-board processor.
- * We cannot use the MPU-401 MIDI system until this firmware
- * has been loaded into the card.
- */
- err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw));
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "firmware device\n");
- goto _release_dma;
- }
- strlcpy(sscape->hw->name, "SoundScape M68K",
- sizeof(sscape->hw->name));
- sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0';
- sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE;
- sscape->hw->ops.open = sscape_hw_open;
- sscape->hw->ops.release = sscape_hw_release;
- sscape->hw->ops.ioctl = sscape_hw_ioctl;
- sscape->hw->private_data = sscape;
+ printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
+ name, sscape->io_base, irq[dev], dma[dev]);
+
+ /*
+ * Check that the user didn't pass us garbage data ...
+ */
+ irq_cfg = get_irq_config(sscape->type, irq[dev]);
+ if (irq_cfg == INVALID_IRQ) {
+ snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
+ return -ENXIO;
+ }
+
+ mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
+ if (mpu_irq_cfg == INVALID_IRQ) {
+ snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
+ return -ENXIO;
}
/*
@@ -1141,9 +1035,6 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
*/
spin_lock_irqsave(&sscape->lock, flags);
- activate_ad1845_unsafe(sscape->io_base);
-
- sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
@@ -1151,15 +1042,23 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
* Enable and configure the DMA channels ...
*/
sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
- dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40);
+ dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
- sscape_write_unsafe(sscape->io_base,
- GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg);
+ mpu_irq_cfg |= mpu_irq_cfg << 2;
+ val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
+ if (joystick[dev])
+ val |= 8;
+ sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
+ sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
sscape_write_unsafe(sscape->io_base,
GA_CDCFG_REG, 0x09 | DMA_8BIT
| (dma[dev] << 4) | (irq_cfg << 1));
+ /*
+ * Enable the master IRQ ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
spin_unlock_irqrestore(&sscape->lock, flags);
@@ -1170,32 +1069,56 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
err = create_ad1845(card, wss_port[dev], irq[dev],
dma[dev], dma2[dev]);
if (err < 0) {
- printk(KERN_ERR "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
- wss_port[dev], irq[dev]);
+ snd_printk(KERN_ERR
+ "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
+ wss_port[dev], irq[dev]);
goto _release_dma;
}
+ strcpy(card->driver, "SoundScape");
+ strcpy(card->shortname, name);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
+ name, sscape->chip->port, sscape->chip->irq,
+ sscape->chip->dma1, sscape->chip->dma2);
+
#define MIDI_DEVNUM 0
if (sscape->type != SSCAPE_VIVO) {
- err = create_mpu401(card, MIDI_DEVNUM, port[dev], mpu_irq[dev]);
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%lx\n",
- port[dev]);
- goto _release_dma;
- }
+ err = sscape_upload_bootblock(card);
+ if (err >= 0)
+ err = sscape_upload_microcode(card, err);
- /*
- * Enable the master IRQ ...
- */
- sscape_write(sscape, GA_INTENA_REG, 0x80);
+ if (err == 0) {
+ err = create_mpu401(card, MIDI_DEVNUM, port[dev],
+ mpu_irq[dev]);
+ if (err < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to create "
+ "MPU-401 device at 0x%lx\n",
+ port[dev]);
+ goto _release_dma;
+ }
- /*
- * Initialize mixer
- */
- sscape->midi_vol = 0;
- host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
- host_write_ctrl_unsafe(sscape->io_base, 0, 100);
- host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+ /*
+ * Initialize mixer
+ */
+ spin_lock_irqsave(&sscape->lock, flags);
+ sscape->midi_vol = 0;
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_XXX_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_EXTMIDI, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ 0, 100);
+ host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+
+ set_midi_mode_unsafe(sscape->io_base);
+ spin_unlock_irqrestore(&sscape->lock, flags);
+ }
}
/*
@@ -1231,7 +1154,8 @@ static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
mpu_irq[i] == SNDRV_AUTO_IRQ ||
dma[i] == SNDRV_AUTO_DMA) {
printk(KERN_INFO
- "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
+ "sscape: insufficient parameters, "
+ "need IO, IRQ, MPU-IRQ and DMA\n");
return 0;
}
@@ -1253,13 +1177,15 @@ static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
sscape->type = SSCAPE;
dma[dev] &= 0x03;
+ snd_card_set_dev(card, pdev);
+
ret = create_sscape(dev, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, pdev);
- if ((ret = snd_card_register(card)) < 0) {
- printk(KERN_ERR "sscape: Failed to register sound card\n");
+ ret = snd_card_register(card);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
}
dev_set_drvdata(pdev, card);
@@ -1311,36 +1237,20 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
* Allow this function to fail *quietly* if all the ISA PnP
* devices were configured using module parameters instead.
*/
- if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS)
+ idx = get_next_autoindex(idx);
+ if (idx >= SNDRV_CARDS)
return -ENOSPC;
/*
- * We have found a candidate ISA PnP card. Now we
- * have to check that it has the devices that we
- * expect it to have.
- *
- * We will NOT try and autoconfigure all of the resources
- * needed and then activate the card as we are assuming that
- * has already been done at boot-time using /proc/isapnp.
- * We shall simply try to give each active card the resources
- * that it wants. This is a sensible strategy for a modular
- * system where unused modules are unloaded regularly.
- *
- * This strategy is utterly useless if we compile the driver
- * into the kernel, of course.
- */
- // printk(KERN_INFO "sscape: %s\n", card->name);
-
- /*
* Check that we still have room for another sound card ...
*/
dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
- if (! dev)
+ if (!dev)
return -ENODEV;
if (!pnp_is_active(dev)) {
if (pnp_activate_dev(dev) < 0) {
- printk(KERN_INFO "sscape: device is inactive\n");
+ snd_printk(KERN_INFO "sscape: device is inactive\n");
return -EBUSY;
}
}
@@ -1378,14 +1288,15 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
wss_port[idx] = pnp_port_start(dev, 1);
dma2[idx] = pnp_dma(dev, 1);
}
+ snd_card_set_dev(card, &pcard->card->dev);
ret = create_sscape(idx, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, &pcard->card->dev);
- if ((ret = snd_card_register(card)) < 0) {
- printk(KERN_ERR "sscape: Failed to register sound card\n");
+ ret = snd_card_register(card);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
}
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 5d2ba1b749a..5b9d6c18bc4 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1682,7 +1682,7 @@ static void snd_wss_resume(struct snd_wss *chip)
}
#endif /* CONFIG_PM */
-int snd_wss_free(struct snd_wss *chip)
+static int snd_wss_free(struct snd_wss *chip)
{
release_and_free_resource(chip->res_port);
release_and_free_resource(chip->res_cport);
@@ -1705,7 +1705,6 @@ int snd_wss_free(struct snd_wss *chip)
kfree(chip);
return 0;
}
-EXPORT_SYMBOL(snd_wss_free);
static int snd_wss_dev_free(struct snd_device *device)
{
@@ -2198,84 +2197,61 @@ EXPORT_SYMBOL(snd_wss_put_double);
static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static struct snd_kcontrol_new snd_ad1848_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT,
- 7, 7, 1, 1),
+static struct snd_kcontrol_new snd_wss_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0,
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
- db_scale_6bit),
+ CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
WSS_DOUBLE("Aux Playback Switch", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Aux Playback Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
+ CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE("Aux Playback Switch", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Aux Playback Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
- db_scale_5bit_12db_max),
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
0, 0, 15, 0, db_scale_rec_gain),
{
- .name = "Capture Source",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
.info = snd_wss_info_mux,
.get = snd_wss_get_mux,
.put = snd_wss_put_mux,
},
-WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0,
- db_scale_6bit),
-};
-
-static struct snd_kcontrol_new snd_wss_controls[] = {
-WSS_DOUBLE("PCM Playback Switch", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
- CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Mic Boost (+20dB)", 0,
+ CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_SINGLE("Loopback Capture Switch", 0,
+ CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1,
+ db_scale_6bit),
WSS_DOUBLE("Line Playback Switch", 0,
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Line Playback Volume", 0,
- CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
- CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_DOUBLE("Aux Playback Switch", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 1,
- CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-WSS_SINGLE("Mono Playback Switch", 0,
+WSS_DOUBLE_TLV("Line Playback Volume", 0,
+ CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
+WSS_SINGLE("Beep Playback Switch", 0,
CS4231_MONO_CTRL, 7, 1, 1),
-WSS_SINGLE("Mono Playback Volume", 0,
- CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE_TLV("Beep Playback Volume", 0,
+ CS4231_MONO_CTRL, 0, 15, 1,
+ db_scale_4bit),
WSS_SINGLE("Mono Output Playback Switch", 0,
CS4231_MONO_CTRL, 6, 1, 1),
-WSS_SINGLE("Mono Output Playback Bypass", 0,
+WSS_SINGLE("Beep Bypass Playback Switch", 0,
CS4231_MONO_CTRL, 5, 1, 0),
-WSS_DOUBLE("Capture Volume", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = snd_wss_info_mux,
- .get = snd_wss_get_mux,
- .put = snd_wss_put_mux,
-},
-WSS_DOUBLE("Mic Boost", 0,
- CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-WSS_SINGLE("Loopback Capture Switch", 0,
- CS4231_LOOPBACK, 0, 1, 0),
-WSS_SINGLE("Loopback Capture Volume", 0,
- CS4231_LOOPBACK, 2, 63, 1)
};
static struct snd_kcontrol_new snd_opti93x_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Master Playback Volume", 0,
- OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+ OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
+ db_scale_6bit),
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE("PCM Playback Volume", 0,
@@ -2334,22 +2310,21 @@ int snd_wss_mixer(struct snd_wss *chip)
if (err < 0)
return err;
}
- else if (chip->hardware & WSS_HW_AD1848_MASK)
- for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_ad1848_controls[idx],
- chip));
- if (err < 0)
- return err;
- }
- else
- for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) {
+ else {
+ int count = ARRAY_SIZE(snd_wss_controls);
+
+ /* Use only the first 11 entries on AD1848 */
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ count = 11;
+
+ for (idx = 0; idx < count; idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(&snd_wss_controls[idx],
chip));
if (err < 0)
return err;
}
+ }
return 0;
}
EXPORT_SYMBOL(snd_wss_mixer);
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 8691f4cf619..f1d9d16b548 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -609,7 +609,7 @@ static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
/* alloc virtual 'dma' area */
if (runtime->dma_area)
vfree(runtime->dma_area);
- runtime->dma_area = vmalloc(size);
+ runtime->dma_area = vmalloc_user(size);
if (runtime->dma_area == NULL)
return -ENOMEM;
runtime->dma_bytes = size;
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index bcf2a0698d5..a513651fa14 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -1,5 +1,3 @@
-# drivers/sound/Config.in
-#
# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
# More hacking for modularisation.
#
@@ -287,18 +285,6 @@ config SOUND_DMAP
Say Y unless you have 16MB or more RAM or a PCI sound card.
-config SOUND_SSCAPE
- tristate "Ensoniq SoundScape support"
- help
- Answer Y if you have a sound card based on the Ensoniq SoundScape
- chipset. Such cards are being manufactured at least by Ensoniq, Spea
- and Reveal (Reveal makes also other cards).
-
- If you compile the driver into the kernel, you have to add
- "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
- line.
-
-
config SOUND_VMIDI
tristate "Loopback MIDI device support"
help
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index e0ae4d4d6a5..567b8a74178 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_MSS) += ad1848.o
obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
diff --git a/sound/oss/audio.c b/sound/oss/audio.c
index b69c05b7ea7..7df48a25c4e 100644
--- a/sound/oss/audio.c
+++ b/sound/oss/audio.c
@@ -838,7 +838,7 @@ static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
if ((err = audio_devs[dev]->d->prepare_for_input(dev,
dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
spin_unlock_irqrestore(&dmap_in->lock,flags);
- return -err;
+ return err;
}
dmap_in->dma_mode = DMODE_INPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 793b7f47843..3f3c3f71db4 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -219,7 +219,9 @@ static int shared_resources_initialised;
* Mid level stuff
*/
-struct sound_settings dmasound = { .lock = SPIN_LOCK_UNLOCKED };
+struct sound_settings dmasound = {
+ .lock = __SPIN_LOCK_UNLOCKED(dmasound.lock)
+};
static inline void sound_silence(void)
{
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 06e9e88e4c0..bb14e4c67e8 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -657,7 +657,7 @@ static int AmiStateInfo(char *buffer, size_t space)
len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
dmasound.volume_right);
if (len >= space) {
- printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
+ printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
len = space ;
}
return len;
diff --git a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c
index 5460faae98c..041ef5c52bc 100644
--- a/sound/oss/hex2hex.c
+++ b/sound/oss/hex2hex.c
@@ -12,7 +12,7 @@
#define MAX_SIZE (256*1024)
unsigned char buf[MAX_SIZE];
-int loadhex(FILE *inf, unsigned char *buf)
+static int loadhex(FILE *inf, unsigned char *buf)
{
int l=0, c, i;
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
index 9e450988ed3..3bc7104c537 100644
--- a/sound/oss/midi_synth.c
+++ b/sound/oss/midi_synth.c
@@ -426,7 +426,7 @@ midi_synth_open(int dev, int mode)
int err;
struct midi_input_info *inc;
- if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+ if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
return -ENXIO;
midi2synth[orig_dev] = dev;
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index 734b8f9e2f7..0af9d24feb8 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -770,7 +770,7 @@ static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
midi_dev = synth_devs[dev]->midi_dev;
- if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+ if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
return -ENXIO;
devc = &dev_conf[midi_dev];
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
index 83f5ee236b1..e19dd5dcc2d 100644
--- a/sound/oss/pss.c
+++ b/sound/oss/pss.c
@@ -269,7 +269,7 @@ static int pss_reset_dsp(pss_confdata * devc)
unsigned long i, limit = jiffies + HZ/10;
outw(0x2000, REG(PSS_CONTROL));
- for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
+ for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
inw(REG(PSS_CONTROL));
outw(0x0000, REG(PSS_CONTROL));
return 1;
@@ -369,11 +369,11 @@ static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size
outw(0, REG(PSS_DATA));
limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && (limit - jiffies >= 0); i++)
+ for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
val = inw(REG(PSS_STATUS));
limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
+ for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
{
val = inw(REG(PSS_STATUS));
if (val & 0x4000)
diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c
index 77d0e5efda7..ce4db49291f 100644
--- a/sound/oss/sb_common.c
+++ b/sound/oss/sb_common.c
@@ -157,7 +157,7 @@ static void sb_intr (sb_devc *devc)
break;
default:
- /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */
+ /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
;
}
}
@@ -177,7 +177,7 @@ static void sb_intr (sb_devc *devc)
break;
default:
- /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */
+ /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
;
}
}
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
index 180e95c87e3..51a3d381a59 100644
--- a/sound/oss/sb_ess.c
+++ b/sound/oss/sb_ess.c
@@ -782,7 +782,7 @@ printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
break;
default:;
- /* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */
+ /* printk(KERN_WARNING "ESS: Unexpected interrupt\n"); */
}
}
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
index b2ed8757542..4153752507e 100644
--- a/sound/oss/sh_dac_audio.c
+++ b/sound/oss/sh_dac_audio.c
@@ -164,9 +164,6 @@ static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
int free;
int nbytes;
- if (count < 0)
- return -EINVAL;
-
if (!count) {
dac_audio_sync();
return 0;
diff --git a/sound/oss/sscape.c b/sound/oss/sscape.c
deleted file mode 100644
index 30c36d1f35d..00000000000
--- a/sound/oss/sscape.c
+++ /dev/null
@@ -1,1480 +0,0 @@
-/*
- * sound/oss/sscape.c
- *
- * Low level driver for Ensoniq SoundScape
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Sergey Smitienko : ensoniq p'n'p support
- * Christoph Hellwig : adapted to module_init/module_exit
- * Bartlomiej Zolnierkiewicz : added __init to attach_sscape()
- * Chris Rankin : Specify that this module owns the coprocessor
- * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/ctype.h>
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-
-#include "coproc.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-/*
- * I/O ports
- */
-#define MIDI_DATA 0
-#define MIDI_CTRL 1
-#define HOST_CTRL 2
-#define TX_READY 0x02
-#define RX_READY 0x01
-#define HOST_DATA 3
-#define ODIE_ADDR 4
-#define ODIE_DATA 5
-
-/*
- * Indirect registers
- */
-
-#define GA_INTSTAT_REG 0
-#define GA_INTENA_REG 1
-#define GA_DMAA_REG 2
-#define GA_DMAB_REG 3
-#define GA_INTCFG_REG 4
-#define GA_DMACFG_REG 5
-#define GA_CDCFG_REG 6
-#define GA_SMCFGA_REG 7
-#define GA_SMCFGB_REG 8
-#define GA_HMCTL_REG 9
-
-/*
- * DMA channel identifiers (A and B)
- */
-
-#define SSCAPE_DMA_A 0
-#define SSCAPE_DMA_B 1
-
-#define PORT(name) (devc->base+name)
-
-/*
- * Host commands recognized by the OBP microcode
- */
-
-#define CMD_GEN_HOST_ACK 0x80
-#define CMD_GEN_MPU_ACK 0x81
-#define CMD_GET_BOARD_TYPE 0x82
-#define CMD_SET_CONTROL 0x88 /* Old firmware only */
-#define CMD_GET_CONTROL 0x89 /* Old firmware only */
-#define CTL_MASTER_VOL 0
-#define CTL_MIC_MODE 2
-#define CTL_SYNTH_VOL 4
-#define CTL_WAVE_VOL 7
-#define CMD_SET_EXTMIDI 0x8a
-#define CMD_GET_EXTMIDI 0x8b
-#define CMD_SET_MT32 0x8c
-#define CMD_GET_MT32 0x8d
-
-#define CMD_ACK 0x80
-
-#define IC_ODIE 1
-#define IC_OPUS 2
-
-typedef struct sscape_info
-{
- int base, irq, dma;
-
- int codec, codec_irq; /* required to setup pnp cards*/
- int codec_type;
- int ic_type;
- char* raw_buf;
- unsigned long raw_buf_phys;
- int buffsize; /* -------------------------- */
- spinlock_t lock;
- int ok; /* Properly detected */
- int failed;
- int dma_allocated;
- int codec_audiodev;
- int opened;
- int *osp;
- int my_audiodev;
-} sscape_info;
-
-static struct sscape_info adev_info = {
- 0
-};
-
-static struct sscape_info *devc = &adev_info;
-static int sscape_mididev = -1;
-
-/* Some older cards have assigned interrupt bits differently than new ones */
-static char valid_interrupts_old[] = {
- 9, 7, 5, 15
-};
-
-static char valid_interrupts_new[] = {
- 9, 5, 7, 10
-};
-
-static char *valid_interrupts = valid_interrupts_new;
-
-/*
- * See the bottom of the driver. This can be set by spea =0/1.
- */
-
-#ifdef REVEAL_SPEA
-static char old_hardware = 1;
-#else
-static char old_hardware;
-#endif
-
-static void sleep(unsigned howlong)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(howlong);
-}
-
-static unsigned char sscape_read(struct sscape_info *devc, int reg)
-{
- unsigned long flags;
- unsigned char val;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb(reg, PORT(ODIE_ADDR));
- val = inb(PORT(ODIE_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return val;
-}
-
-static void __sscape_write(int reg, int data)
-{
- outb(reg, PORT(ODIE_ADDR));
- outb(data, PORT(ODIE_DATA));
-}
-
-static void sscape_write(struct sscape_info *devc, int reg, int data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- __sscape_write(reg, data);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg)
-{
- unsigned char res;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- res = inb (devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
- return res;
-
-}
-
-static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- outb( reg, devc -> codec);
- outb( data, devc -> codec + 1);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void host_open(struct sscape_info *devc)
-{
- outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */
-}
-
-static void host_close(struct sscape_info *devc)
-{
- outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */
-}
-
-static int host_write(struct sscape_info *devc, unsigned char *data, int count)
-{
- unsigned long flags;
- int i, timeout_val;
-
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Send the command and data bytes
- */
-
- for (i = 0; i < count; i++)
- {
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & TX_READY)
- break;
-
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- outb(data[i], PORT(HOST_DATA));
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return 1;
-}
-
-static int host_read(struct sscape_info *devc)
-{
- unsigned long flags;
- int timeout_val;
- unsigned char data;
-
- spin_lock_irqsave(&devc->lock,flags);
- /*
- * Read a byte
- */
-
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb(PORT(HOST_CTRL)) & RX_READY)
- break;
-
- if (timeout_val <= 0)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -1;
- }
- data = inb(PORT(HOST_DATA));
- spin_unlock_irqrestore(&devc->lock,flags);
- return data;
-}
-
-#if 0 /* unused */
-static int host_command1(struct sscape_info *devc, int cmd)
-{
- unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- return host_write(devc, buf, 1);
-}
-#endif /* unused */
-
-
-static int host_command2(struct sscape_info *devc, int cmd, int parm1)
-{
- unsigned char buf[10];
-
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
-
- return host_write(devc, buf, 2);
-}
-
-static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
-{
- unsigned char buf[10];
-
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
- buf[2] = (unsigned char) (parm2 & 0xff);
- return host_write(devc, buf, 3);
-}
-
-static void set_mt32(struct sscape_info *devc, int value)
-{
- host_open(devc);
- host_command2(devc, CMD_SET_MT32, value ? 1 : 0);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
- }
- host_close(devc);
-}
-
-static void set_control(struct sscape_info *devc, int ctrl, int value)
-{
- host_open(devc);
- host_command3(devc, CMD_SET_CONTROL, ctrl, value);
- if (host_read(devc) != CMD_ACK)
- {
- /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */
- }
- host_close(devc);
-}
-
-static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
-{
- unsigned char temp;
-
- if (dma_chan != SSCAPE_DMA_A)
- {
- printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n");
- return;
- }
- audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
- DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode);
- audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
-
- temp = devc->dma << 4; /* Setup DMA channel select bits */
- if (devc->dma <= 3)
- temp |= 0x80; /* 8 bit DMA channel */
-
- temp |= 1; /* Trigger DMA */
- sscape_write(devc, GA_DMAA_REG, temp);
- temp &= 0xfe; /* Clear DMA trigger */
- sscape_write(devc, GA_DMAA_REG, temp);
-}
-
-static int verify_mpu(struct sscape_info *devc)
-{
- /*
- * The SoundScape board could be in three modes (MPU, 8250 and host).
- * If the card is not in the MPU mode, enabling the MPU driver will
- * cause infinite loop (the driver believes that there is always some
- * received data in the buffer.
- *
- * Detect this by looking if there are more than 10 received MIDI bytes
- * (0x00) in the buffer.
- */
-
- int i;
-
- for (i = 0; i < 10; i++)
- {
- if (inb(devc->base + HOST_CTRL) & 0x80)
- return 1;
-
- if (inb(devc->base) != 0x00)
- return 1;
- }
- printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n");
- return 0;
-}
-
-static int sscape_coproc_open(void *dev_info, int sub_device)
-{
- if (sub_device == COPR_MIDI)
- {
- set_mt32(devc, 0);
- if (!verify_mpu(devc))
- return -EIO;
- }
- return 0;
-}
-
-static void sscape_coproc_close(void *dev_info, int sub_device)
-{
- struct sscape_info *devc = dev_info;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (devc->dma_allocated)
- {
- __sscape_write(GA_DMAA_REG, 0x20); /* DMA channel disabled */
- devc->dma_allocated = 0;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return;
-}
-
-static void sscape_coproc_reset(void *dev_info)
-{
-}
-
-static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
-{
- unsigned long flags;
- unsigned char temp;
- volatile int done, timeout_val;
- static unsigned char codec_dma_bits;
-
- if (flag & CPF_FIRST)
- {
- /*
- * First block. Have to allocate DMA and to reset the board
- * before continuing.
- */
-
- spin_lock_irqsave(&devc->lock,flags);
- codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
-
- if (devc->dma_allocated == 0)
- devc->dma_allocated = 1;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
-
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- sscape_read(devc, GA_HMCTL_REG); /* Delay */
-
- /* Take board out of reset */
- sscape_write(devc, GA_HMCTL_REG,
- (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
- }
- /*
- * Transfer one code block using DMA
- */
- if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
- {
- printk(KERN_WARNING "soundscape: DMA buffer not available\n");
- return 0;
- }
- memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
-
- spin_lock_irqsave(&devc->lock,flags);
-
- /******** INTERRUPTS DISABLED NOW ********/
-
- do_dma(devc, SSCAPE_DMA_A,
- audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
- size, DMA_MODE_WRITE);
-
- /*
- * Wait until transfer completes.
- */
-
- done = 0;
- timeout_val = 30;
- while (!done && timeout_val-- > 0)
- {
- int resid;
-
- if (HZ / 50)
- sleep(HZ / 50);
- clear_dma_ff(devc->dma);
- if ((resid = get_dma_residue(devc->dma)) == 0)
- done = 1;
- }
-
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- return 0;
-
- if (flag & CPF_LAST)
- {
- /*
- * Take the board out of reset
- */
- outb((0x00), PORT(HOST_CTRL));
- outb((0x00), PORT(MIDI_CTRL));
-
- temp = sscape_read(devc, GA_HMCTL_REG);
- temp |= 0x40;
- sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */
-
- /*
- * Wait until the ODB wakes up
- */
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
-
- sleep(1);
- x = inb(PORT(HOST_DATA));
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- DDB(printk("Soundscape: Acknowledge = %x\n", x));
- done = 1;
- }
- }
- sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
-
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n");
- return 0;
- }
- spin_lock_irqsave(&devc->lock,flags);
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- sleep(1);
- if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */
- done = 1;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- if (!done)
- {
- printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
- return 0;
- }
- printk(KERN_INFO "SoundScape board initialized OK\n");
- set_control(devc, CTL_MASTER_VOL, 100);
- set_control(devc, CTL_SYNTH_VOL, 100);
-
-#ifdef SSCAPE_DEBUG3
- /*
- * Temporary debugging aid. Print contents of the registers after
- * downloading the code.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
-#endif
-
- }
- return 1;
-}
-
-static int download_boot_block(void *dev_info, copr_buffer * buf)
-{
- if (buf->len <= 0 || buf->len > sizeof(buf->data))
- return -EINVAL;
-
- if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
- {
- printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n");
- return -EIO;
- }
- return 0;
-}
-
-static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
-{
- copr_buffer *buf;
- int err;
-
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- sscape_coproc_reset(dev_info);
- return 0;
-
- case SNDCTL_COPR_LOAD:
- buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- if (copy_from_user(buf, arg, sizeof(copr_buffer)))
- {
- vfree(buf);
- return -EFAULT;
- }
- err = download_boot_block(dev_info, buf);
- vfree(buf);
- return err;
-
- default:
- return -EINVAL;
- }
-}
-
-static coproc_operations sscape_coproc_operations =
-{
- "SoundScape M68K",
- THIS_MODULE,
- sscape_coproc_open,
- sscape_coproc_close,
- sscape_coproc_ioctl,
- sscape_coproc_reset,
- &adev_info
-};
-
-static struct resource *sscape_ports;
-static int sscape_is_pnp;
-
-static void __init attach_sscape(struct address_info *hw_config)
-{
-#ifndef SSCAPE_REGS
- /*
- * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
- * These values are card
- * dependent. If you have another SoundScape based card, you have to
- * find the correct values. Do the following:
- * - Compile this driver with SSCAPE_DEBUG1 defined.
- * - Shut down and power off your machine.
- * - Boot with DOS so that the SSINIT.EXE program is run.
- * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
- * when detecting the SoundScape.
- * - Modify the following list to use the values printed during boot.
- * Undefine the SSCAPE_DEBUG1
- */
-#define SSCAPE_REGS { \
-/* I0 */ 0x00, \
-/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \
-/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \
-/* I4 */ 0xf5, /* Ignored */ \
-/* I5 */ 0x10, \
-/* I6 */ 0x00, \
-/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \
-/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \
-/* I9 */ 0x40 /* Ignored */ \
- }
-#endif
-
- unsigned long flags;
- static unsigned char regs[10] = SSCAPE_REGS;
-
- int i, irq_bits = 0xff;
-
- if (old_hardware)
- {
- valid_interrupts = valid_interrupts_old;
- conf_printf("Ensoniq SoundScape (old)", hw_config);
- }
- else
- conf_printf("Ensoniq SoundScape", hw_config);
-
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
- {
- printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
- return;
- }
-
- if (!sscape_is_pnp) {
-
- spin_lock_irqsave(&devc->lock,flags);
- /* Host interrupt enable */
- sscape_write(devc, 1, 0xf0); /* All interrupts enabled */
- /* DMA A status/trigger register */
- sscape_write(devc, 2, 0x20); /* DMA channel disabled */
- /* DMA B status/trigger register */
- sscape_write(devc, 3, 0x20); /* DMA channel disabled */
- /* Host interrupt config reg */
- sscape_write(devc, 4, 0xf0 | (irq_bits << 2) | irq_bits);
- /* Don't destroy CD-ROM DMA config bits (0xc0) */
- sscape_write(devc, 5, (regs[5] & 0x3f) | (sscape_read(devc, 5) & 0xc0));
- /* CD-ROM config (WSS codec actually) */
- sscape_write(devc, 6, regs[6]);
- sscape_write(devc, 7, regs[7]);
- sscape_write(devc, 8, regs[8]);
- /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
- sscape_write(devc, 9, (sscape_read(devc, 9) & 0xf0) | 0x08);
- spin_unlock_irqrestore(&devc->lock,flags);
- }
-#ifdef SSCAPE_DEBUG2
- /*
- * Temporary debugging aid. Print contents of the registers after
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
- }
-#endif
-
- if (probe_mpu401(hw_config, sscape_ports))
- hw_config->always_detect = 1;
- hw_config->name = "SoundScape";
-
- hw_config->irq *= -1; /* Negative value signals IRQ sharing */
- attach_mpu401(hw_config, THIS_MODULE);
- hw_config->irq *= -1; /* Restore it */
-
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- {
- sscape_mididev = hw_config->slots[1];
- midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
- }
- sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
- devc->ok = 1;
- devc->failed = 0;
-}
-
-static int detect_ga(sscape_info * devc)
-{
- unsigned char save;
-
- DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
-
- /*
- * First check that the address register of "ODIE" is
- * there and that it has exactly 4 writable bits.
- * First 4 bits
- */
-
- if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
- {
- DDB(printk("soundscape: Detect error A\n"));
- return 0;
- }
- outb((0x00), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x00)
- {
- DDB(printk("soundscape: Detect error B\n"));
- return 0;
- }
- outb((0xff), PORT(ODIE_ADDR));
- if (inb(PORT(ODIE_ADDR)) != 0x0f)
- {
- DDB(printk("soundscape: Detect error C\n"));
- return 0;
- }
- outb((save), PORT(ODIE_ADDR));
-
- /*
- * Now verify that some indirect registers return zero on some bits.
- * This may break the driver with some future revisions of "ODIE" but...
- */
-
- if (sscape_read(devc, 0) & 0x0c)
- {
- DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
- return 0;
- }
- if (sscape_read(devc, 1) & 0x0f)
- {
- DDB(printk("soundscape: Detect error E\n"));
- return 0;
- }
- if (sscape_read(devc, 5) & 0x0f)
- {
- DDB(printk("soundscape: Detect error F\n"));
- return 0;
- }
- return 1;
-}
-
-static int sscape_read_host_ctrl(sscape_info* devc)
-{
- return host_read(devc);
-}
-
-static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b)
-{
- host_command2(devc, a, b);
-}
-
-static int sscape_alloc_dma(sscape_info *devc)
-{
- char *start_addr, *end_addr;
- int dma_pagesize;
- int sz, size;
- struct page *page;
-
- if (devc->raw_buf != NULL) return 0; /* Already done */
- dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024);
- devc->raw_buf = NULL;
- devc->buffsize = 8192*4;
- if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize;
- start_addr = NULL;
- /*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
- while (start_addr == NULL && devc->buffsize > PAGE_SIZE) {
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- devc->buffsize = PAGE_SIZE * (1 << sz);
- start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
- if (start_addr == NULL) devc->buffsize /= 2;
- }
-
- if (start_addr == NULL) {
- printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n");
- return 0;
- } else {
- /* make some checks */
- end_addr = start_addr + devc->buffsize - 1;
- /* now check if it fits into the same dma-pagesize */
-
- if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
- printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize);
- return 0;
- }
- }
- devc->raw_buf = start_addr;
- devc->raw_buf_phys = virt_to_bus(start_addr);
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- SetPageReserved(page);
- return 1;
-}
-
-static void sscape_free_dma(sscape_info *devc)
-{
- int sz, size;
- unsigned long start_addr, end_addr;
- struct page *page;
-
- if (devc->raw_buf == NULL) return;
- for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
- start_addr = (unsigned long) devc->raw_buf;
- end_addr = start_addr + devc->buffsize;
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- ClearPageReserved(page);
-
- free_pages((unsigned long) devc->raw_buf, sz);
- devc->raw_buf = NULL;
-}
-
-/* Intel version !!!!!!!!! */
-
-static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode)
-{
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(chan);
- clear_dma_ff(chan);
- set_dma_mode(chan, dma_mode);
- set_dma_addr(chan, physaddr);
- set_dma_count(chan, count);
- enable_dma(chan);
- release_dma_lock(flags);
- return 0;
-}
-
-static void sscape_pnp_start_dma(sscape_info* devc, int arg )
-{
- int reg;
- if (arg == 0) reg = 2;
- else reg = 3;
-
- sscape_write(devc, reg, sscape_read( devc, reg) | 0x01);
- sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE);
-}
-
-static int sscape_pnp_wait_dma (sscape_info* devc, int arg )
-{
- int reg;
- unsigned long i;
- unsigned char d;
-
- if (arg == 0) reg = 2;
- else reg = 3;
-
- sleep ( 1 );
- i = 0;
- do {
- d = sscape_read(devc, reg) & 1;
- if ( d == 1) break;
- i++;
- } while (i < 500000);
- d = sscape_read(devc, reg) & 1;
- return d;
-}
-
-static int sscape_pnp_alloc_dma(sscape_info* devc)
-{
- /* printk(KERN_INFO "sscape: requesting dma\n"); */
- if (request_dma(devc -> dma, "sscape")) return 0;
- /* printk(KERN_INFO "sscape: dma channel allocated\n"); */
- if (!sscape_alloc_dma(devc)) {
- free_dma(devc -> dma);
- return 0;
- };
- return 1;
-}
-
-static void sscape_pnp_free_dma(sscape_info* devc)
-{
- sscape_free_dma( devc);
- free_dma(devc -> dma );
- /* printk(KERN_INFO "sscape: dma released\n"); */
-}
-
-static int sscape_pnp_upload_file(sscape_info* devc, char* fn)
-{
- int done = 0;
- int timeout_val;
- char* data,*dt;
- int len,l;
- unsigned long flags;
-
- sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F );
- sscape_write( devc, 2, (devc -> dma << 4) | 0x80 );
- sscape_write( devc, 3, 0x20 );
- sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 );
-
- len = mod_firmware_load(fn, &data);
- if (len == 0) {
- printk(KERN_ERR "sscape: file not found: %s\n", fn);
- return 0;
- }
- dt = data;
- spin_lock_irqsave(&devc->lock,flags);
- while ( len > 0 ) {
- if (len > devc -> buffsize) l = devc->buffsize;
- else l = len;
- len -= l;
- memcpy(devc->raw_buf, dt, l); dt += l;
- sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48);
- sscape_pnp_start_dma ( devc, 0 );
- if (sscape_pnp_wait_dma ( devc, 0 ) == 0) {
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- }
-
- spin_unlock_irqrestore(&devc->lock,flags);
- vfree(data);
-
- outb(0, devc -> base + 2);
- outb(0, devc -> base);
-
- sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40);
-
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
- timeout_val = 5 * HZ;
- done = 0;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
- sleep(1);
- x = inb( devc -> base + 3);
- if (x == 0xfe) /* OBP startup acknowledge */
- {
- //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
- done = 1;
- }
- }
-
- if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
-
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, (devc -> dma << 4) + 0x80);
- return 1;
-}
-
-static void __init sscape_pnp_init_hw(sscape_info* devc)
-{
- unsigned char midi_irq = 0, sb_irq = 0;
- unsigned i;
- static char code_file_name[23] = "/sndscape/sndscape.cox";
-
- int sscape_joystic_enable = 0x7f;
- int sscape_mic_enable = 0;
- int sscape_ext_midi = 0;
-
- if ( !sscape_pnp_alloc_dma(devc) ) {
- printk(KERN_ERR "sscape: faild to allocate dma\n");
- return;
- }
-
- for (i = 0; i < 4; i++) {
- if ( devc -> irq == valid_interrupts[i] )
- midi_irq = i;
- if ( devc -> codec_irq == valid_interrupts[i] )
- sb_irq = i;
- }
-
- sscape_write( devc, 5, 0x50);
- sscape_write( devc, 7, 0x2e);
- sscape_write( devc, 8, 0x00);
-
- sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
- sscape_write( devc, 3, ( devc -> dma << 4) | 0x80);
-
- sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq);
-
- i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
- if (sscape_joystic_enable) i |= 8;
-
- sscape_write (devc, 9, i);
- sscape_write (devc, 6, 0x80);
- sscape_write (devc, 1, 0x80);
-
- if (devc -> codec_type == 2) {
- sscape_pnp_write_codec( devc, 0x0C, 0x50);
- sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F);
- sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0);
- sscape_pnp_write_codec( devc, 29, 0x20);
- }
-
- if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) {
- printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n");
- sscape_pnp_free_dma(devc);
- return;
- }
-
- i = sscape_read_host_ctrl( devc );
-
- if ( (i & 0x0F) > 7 ) {
- printk(KERN_ERR "sscape: scope.cod faild\n");
- sscape_pnp_free_dma(devc);
- return;
- }
- if ( i & 0x10 ) sscape_write( devc, 7, 0x2F);
- code_file_name[21] = (char) ( i & 0x0F) + 0x30;
- if (sscape_pnp_upload_file( devc, code_file_name) == 0) {
- printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name);
- sscape_pnp_free_dma(devc);
- return;
- }
-
- if (devc->ic_type != IC_ODIE) {
- sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) |
- ( sscape_mic_enable == 0 ? 0x00 : 0x80) );
- }
- sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */
- sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */
- sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi);
-
- sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL
- sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL
- sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR
-
- if (devc -> codec_type == 1) {
- sscape_pnp_write_codec ( devc, 4, 0x1F );
- sscape_pnp_write_codec ( devc, 5, 0x1F );
- sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable);
- } else {
- int t;
- sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1);
- sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1));
-
- t = sscape_pnp_read_codec( devc, 0x00) & 0xDF;
- if ( (sscape_mic_enable == 0)) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x00, t);
- t = sscape_pnp_read_codec( devc, 0x01) & 0xDF;
- if ( (sscape_mic_enable == 0) ) t |= 0;
- else t |= 0x20;
- sscape_pnp_write_codec ( devc, 0x01, t);
- sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20);
- outb(0, devc -> codec);
- }
- if (devc -> ic_type == IC_OPUS ) {
- int i = sscape_read( devc, 9 );
- sscape_write( devc, 9, i | 3 );
- sscape_write( devc, 3, 0x40);
-
- if (request_region(0x228, 1, "sscape setup junk")) {
- outb(0, 0x228);
- release_region(0x228,1);
- }
- sscape_write( devc, 3, (devc -> dma << 4) | 0x80);
- sscape_write( devc, 9, i );
- }
-
- host_close ( devc );
- sscape_pnp_free_dma(devc);
-}
-
-static int __init detect_sscape_pnp(sscape_info* devc)
-{
- long i, irq_bits = 0xff;
- unsigned int d;
-
- DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base));
-
- if (!request_region(devc->codec, 2, "sscape codec")) {
- printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec);
- return 0;
- }
-
- if ((inb(devc->base + 2) & 0x78) != 0)
- goto fail;
-
- d = inb ( devc -> base + 4) & 0xF0;
- if (d & 0x80)
- goto fail;
-
- if (d == 0) {
- devc->codec_type = 1;
- devc->ic_type = IC_ODIE;
- } else if ( (d & 0x60) != 0) {
- devc->codec_type = 2;
- devc->ic_type = IC_OPUS;
- } else if ( (d & 0x40) != 0) { /* WTF? */
- devc->codec_type = 2;
- devc->ic_type = IC_ODIE;
- } else
- goto fail;
-
- sscape_is_pnp = 1;
-
- outb(0xFA, devc -> base+4);
- if ((inb( devc -> base+4) & 0x9F) != 0x0A)
- goto fail;
- outb(0xFE, devc -> base+4);
- if ( (inb(devc -> base+4) & 0x9F) != 0x0E)
- goto fail;
- if ( (inb(devc -> base+5) & 0x9F) != 0x0E)
- goto fail;
-
- if (devc->codec_type == 2) {
- if (devc->codec != devc->base + 8) {
- printk("soundscape warning: incorrect codec port specified\n");
- goto fail;
- }
- d = 0x10 | (sscape_read(devc, 9) & 0xCF);
- sscape_write(devc, 9, d);
- sscape_write(devc, 6, 0x80);
- } else {
- //todo: check codec is not base + 8
- }
-
- d = (sscape_read(devc, 9) & 0x3F) | 0xC0;
- sscape_write(devc, 9, d);
-
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80) ) break;
-
- d = inb(devc -> codec);
- if (d & 0x80)
- goto fail;
- if ( inb(devc -> codec + 2) == 0xFF)
- goto fail;
-
- sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F );
-
- d = inb(devc -> codec) & 0x80;
- if ( d == 0) {
- printk(KERN_INFO "soundscape: hardware detected\n");
- valid_interrupts = valid_interrupts_new;
- } else {
- printk(KERN_INFO "soundscape: board looks like media fx\n");
- valid_interrupts = valid_interrupts_old;
- old_hardware = 1;
- }
-
- sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) );
-
- for (i = 0; i < 550000; i++)
- if ( !(inb(devc -> codec) & 0x80))
- break;
-
- sscape_pnp_init_hw(devc);
-
- for (i = 0; i < 4; i++)
- {
- if (devc->codec_irq == valid_interrupts[i]) {
- irq_bits = i;
- break;
- }
- }
- sscape_write(devc, GA_INTENA_REG, 0x00);
- sscape_write(devc, GA_DMACFG_REG, 0x50);
- sscape_write(devc, GA_DMAA_REG, 0x70);
- sscape_write(devc, GA_DMAB_REG, 0x20);
- sscape_write(devc, GA_INTCFG_REG, 0xf0);
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1));
-
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20);
- sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20);
-
- return 1;
-fail:
- release_region(devc->codec, 2);
- return 0;
-}
-
-static int __init probe_sscape(struct address_info *hw_config)
-{
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
-
-#ifdef SSCAPE_DEBUG1
- /*
- * Temporary debugging aid. Print contents of the registers before
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
- }
-#endif
- devc->failed = 1;
-
- sscape_ports = request_region(devc->base, 2, "mpu401");
- if (!sscape_ports)
- return 0;
-
- if (!request_region(devc->base + 2, 6, "SoundScape")) {
- release_region(devc->base, 2);
- return 0;
- }
-
- if (!detect_ga(devc)) {
- if (detect_sscape_pnp(devc))
- return 1;
- release_region(devc->base, 2);
- release_region(devc->base + 2, 6);
- return 0;
- }
-
- if (old_hardware) /* Check that it's really an old Spea/Reveal card. */
- {
- unsigned char tmp;
- int cc;
-
- if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
- {
- sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
- for (cc = 0; cc < 200000; ++cc)
- inb(devc->base + ODIE_ADDR);
- }
- }
- return 1;
-}
-
-static int __init init_ss_ms_sound(struct address_info *hw_config)
-{
- int i, irq_bits = 0xff;
- int ad_flags = 0;
- struct resource *ports;
-
- if (devc->failed)
- {
- printk(KERN_ERR "soundscape: Card not detected\n");
- return 0;
- }
- if (devc->ok == 0)
- {
- printk(KERN_ERR "soundscape: Invalid initialization order.\n");
- return 0;
- }
- for (i = 0; i < 4; i++)
- {
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- }
- if (irq_bits == 0xff) {
- printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq);
- return 0;
- }
-
- if (old_hardware)
- ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
- else if (sscape_is_pnp)
- ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */
-
- ports = request_region(hw_config->io_base, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "soundscape: ports busy\n");
- return 0;
- }
-
- if (!ad1848_detect(ports, &ad_flags, hw_config->osp)) {
- release_region(hw_config->io_base, 4);
- return 0;
- }
-
- if (!sscape_is_pnp) /*pnp is already setup*/
- {
- /*
- * Setup the DMA polarity.
- */
- sscape_write(devc, GA_DMACFG_REG, 0x50);
-
- /*
- * Take the gate-array off of the DMA channel.
- */
- sscape_write(devc, GA_DMAB_REG, 0x20);
-
- /*
- * Init the AD1848 (CD-ROM) config reg.
- */
- sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1));
- }
-
- if (hw_config->irq == devc->irq)
- printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n");
-
- hw_config->slots[0] = ad1848_init(
- sscape_is_pnp ? "SoundScape" : "SoundScape PNP",
- ports,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma,
- 0,
- devc->osp,
- THIS_MODULE);
-
-
- if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */
- {
- audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
- devc->codec_audiodev = hw_config->slots[0];
- devc->my_audiodev = hw_config->slots[0];
-
- /* Set proper routings here (what are they) */
- AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- }
-
-#ifdef SSCAPE_DEBUG5
- /*
- * Temporary debugging aid. Print contents of the registers
- * after the AD1848 device has been initialized.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk("I%d = %02x\n", i, sscape_read(devc, i));
- }
-#endif
- return 1;
-}
-
-static void __exit unload_sscape(struct address_info *hw_config)
-{
- release_region(devc->base + 2, 6);
- unload_mpu401(hw_config);
- if (sscape_is_pnp)
- release_region(devc->codec, 2);
-}
-
-static void __exit unload_ss_ms_sound(struct address_info *hw_config)
-{
- ad1848_unload(hw_config->io_base,
- hw_config->irq,
- devc->dma,
- devc->dma,
- 0);
- sound_unload_audiodev(hw_config->slots[0]);
-}
-
-static struct address_info cfg;
-static struct address_info cfg_mpu;
-
-static int __initdata spea = -1;
-static int mss = 0;
-static int __initdata dma = -1;
-static int __initdata irq = -1;
-static int __initdata io = -1;
-static int __initdata mpu_irq = -1;
-static int __initdata mpu_io = -1;
-
-module_param(dma, int, 0);
-module_param(irq, int, 0);
-module_param(io, int, 0);
-module_param(spea, int, 0); /* spea=0/1 set the old_hardware */
-module_param(mpu_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mss, int, 0);
-
-static int __init init_sscape(void)
-{
- printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.io_base = io;
-
- cfg_mpu.irq = mpu_irq;
- cfg_mpu.io_base = mpu_io;
- /* WEH - Try to get right dma channel */
- cfg_mpu.dma = dma;
-
- devc->codec = cfg.io_base;
- devc->codec_irq = cfg.irq;
- devc->codec_type = 0;
- devc->ic_type = 0;
- devc->raw_buf = NULL;
- spin_lock_init(&devc->lock);
-
- if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) {
- printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n");
- return -EINVAL;
- }
-
- if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) {
- printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n");
- return -EINVAL;
- }
-
- if(spea != -1) {
- old_hardware = spea;
- printk(KERN_INFO "Forcing %s hardware support.\n",
- spea?"new":"old");
- }
- if (probe_sscape(&cfg_mpu) == 0)
- return -ENODEV;
-
- attach_sscape(&cfg_mpu);
-
- mss = init_ss_ms_sound(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_sscape(void)
-{
- if (mss)
- unload_ss_ms_sound(&cfg);
- unload_sscape(&cfg_mpu);
-}
-
-module_init(init_sscape);
-module_exit(cleanup_sscape);
-
-#ifndef MODULE
-static int __init setup_sscape(char *str)
-{
- /* io, irq, dma, mpu_io, mpu_irq */
- int ints[6];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- mpu_io = ints[4];
- mpu_irq = ints[5];
-
- return 1;
-}
-
-__setup("sscape=", setup_sscape);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 75c602b5b13..351654cf7b0 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -570,6 +570,7 @@ config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
select SND_MPU401_UART
select SND_AC97_CODEC
+ select BITREVERSE
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 78288dbfc17..c1192062300 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -603,8 +603,8 @@ AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
-AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
-AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+AC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
@@ -1393,7 +1393,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
}
- /* build PC Speaker controls */
+ /* build Beep controls */
if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
@@ -2122,7 +2122,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
}
/* nothing should be in powerdown mode */
snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
- end_time = jiffies + msecs_to_jiffies(120);
+ end_time = jiffies + msecs_to_jiffies(5000);
do {
if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
goto __ready_ok;
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 7337abdbe4e..139cf3b2b9d 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -800,12 +800,12 @@ AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1),
AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
-AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
-AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
-AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1),
-AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1),
-AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1),
-AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1),
+AC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
+AC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
+AC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1),
+AC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1),
+AC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1),
+AC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1),
AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 8451a0169f3..69867ace786 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -830,8 +830,8 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
- AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
- AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
+ AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
+ AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index c8c6f437f5b..8f443a9d61e 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -792,8 +792,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
"Phone Playback Volume",
"Video Playback Switch",
"Video Playback Volume",
- "PC Speaker Playback Switch",
- "PC Speaker Playback Volume",
+ "Beep Playback Switch",
+ "Beep Playback Volume",
"Mono Output Select",
"Capture Source",
"Capture Switch",
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index c62b7d10ec6..0470461cc03 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -233,7 +233,7 @@ static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 val
snd_iprintf(buffer, "user-defined\n");
break;
default:
- snd_iprintf(buffer, "unkown\n");
+ snd_iprintf(buffer, "unknown\n");
break;
}
snd_iprintf(buffer, "Sample Bits: ");
@@ -304,7 +304,7 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
- if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+ if (reg < 0x40 && val <= 0xffffffff) {
spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -405,7 +405,7 @@ static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+ if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)
snd_ca0106_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index ddcd4a9fd7e..a312bae08f5 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2302,7 +2302,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
- CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+ CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
@@ -2310,7 +2310,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
- CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+ CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
};
diff --git a/sound/pci/cs46xx/imgs/cwcdma.asp b/sound/pci/cs46xx/imgs/cwcdma.asp
index 09d24c76f03..a65e1193c89 100644
--- a/sound/pci/cs46xx/imgs/cwcdma.asp
+++ b/sound/pci/cs46xx/imgs/cwcdma.asp
@@ -26,10 +26,11 @@
//
//
// The purpose of this code is very simple: make it possible to tranfser
-// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host)
-// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters)
-// task always alters the samples in some how, however it's from 48khz -> 48khz. The
-// alterations are not audible, but AC3 wont work.
+// the samples 'as they are' with no alteration from a PCMreader
+// SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF.
+// SRC (source rate converters) task always alters the samples in somehow,
+// however it's from 48khz -> 48khz.
+// The alterations are not audible, but AC3 wont work.
//
// ...
// |
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index fda7a94c992..ccc642269b9 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -4,9 +4,7 @@
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
-ifdef CONFIG_MGEODE_LX
snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
-endif
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 05f56e04849..91e7faf69bb 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -389,6 +389,7 @@ probefail_out:
static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
{
+ olpc_quirks_cleanup();
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 7a298ac662e..51966d782a3 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -99,10 +99,11 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
int snd_cs5535audio_resume(struct pci_dev *pci);
#endif
-#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
+#ifdef CONFIG_OLPC
void __devinit olpc_prequirks(struct snd_card *card,
struct snd_ac97_template *ac97);
int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void __devexit olpc_quirks_cleanup(void);
void olpc_analog_input(struct snd_ac97 *ac97, int on);
void olpc_mic_bias(struct snd_ac97 *ac97, int on);
@@ -128,6 +129,7 @@ static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
{
return 0;
}
+static inline void olpc_quirks_cleanup(void) { }
static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
index 5c6814335cd..50da49be9ae 100644
--- a/sound/pci/cs5535audio/cs5535audio_olpc.c
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -13,10 +13,13 @@
#include <sound/info.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
+#include <linux/gpio.h>
#include <asm/olpc.h>
#include "cs5535audio.h"
+#define DRV_NAME "cs5535audio-olpc"
+
/*
* OLPC has an additional feature on top of the regular AD1888 codec features.
* It has an Analog Input mode that is switched into (after disabling the
@@ -38,10 +41,7 @@ void olpc_analog_input(struct snd_ac97 *ac97, int on)
}
/* set Analog Input through GPIO */
- if (on)
- geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
- else
- geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+ gpio_set_value(OLPC_GPIO_MIC_AC, on);
}
/*
@@ -73,8 +73,7 @@ static int olpc_dc_info(struct snd_kcontrol *kctl,
static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
{
- v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
- GPIO_OUTPUT_VAL);
+ v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);
return 0;
}
@@ -153,6 +152,12 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
if (!machine_is_olpc())
return 0;
+ if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
+ printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");
+ return -EIO;
+ }
+ gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
+
/* drop the original AD1888 HPF control */
memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -169,11 +174,18 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
ac97->private_data));
- if (err < 0)
+ if (err < 0) {
+ gpio_free(OLPC_GPIO_MIC_AC);
return err;
+ }
}
/* turn off the mic by default */
olpc_mic_bias(ac97, 0);
return 0;
}
+
+void __devexit olpc_quirks_cleanup(void)
+{
+ gpio_free(OLPC_GPIO_MIC_AC);
+}
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 75454648d50..cb65bd0dd35 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -240,7 +240,7 @@ static int select_rom(unsigned int pitch)
} else if (pitch == 0x02000000) {
/* pitch == 2 */
return 3;
- } else if (pitch >= 0x0 && pitch <= 0x08000000) {
+ } else if (pitch <= 0x08000000) {
/* 0 <= pitch <= 8 */
return 0;
} else {
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 36e08bd2b3c..1d369ff7380 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -184,7 +184,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
* The hardware has 3 channels for playback and 1 for capture.
* - channel 0 is the front channel
* - channel 1 is the rear channel
- * - channel 2 is the center/lfe chanel
+ * - channel 2 is the center/lfe channel
* Volume is controlled by the AC97 for the front and rear channels by
* the PCM Playback Volume, Sigmatel Surround Playback Volume and
* Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
@@ -1040,8 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff)
- && (channel_id >= 0) && (channel_id <= 2) )
+ if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index b0fb6c917c3..05afe06e353 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1818,8 +1818,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Master Playback Switch", "Master Capture Switch",
"Master Playback Volume", "Master Capture Volume",
"Wave Master Playback Volume", "Master Playback Volume",
- "PC Speaker Playback Switch", "PC Speaker Capture Switch",
- "PC Speaker Playback Volume", "PC Speaker Capture Volume",
+ "Beep Playback Switch", "Beep Capture Switch",
+ "Beep Playback Volume", "Beep Capture Volume",
"Phone Playback Switch", "Phone Capture Switch",
"Phone Playback Volume", "Phone Capture Volume",
"Mic Playback Switch", "Mic Capture Switch",
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 216f9748aff..baa7cd508cd 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -451,7 +451,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
- if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) {
+ if (reg < 0x40 && val <= 0xffffffff) {
spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -527,7 +527,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) )
+ if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
snd_ptr_write(emu, iobase, reg, channel_id, val);
}
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index c1a5aa15af8..5ef7080e14d 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -256,7 +256,7 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
if (reg > 0x3f)
return 1;
reg += 0x40; /* 0x40 upwards are registers. */
- if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+ if (value > 0x3f) /* 0 to 0x3f are values */
return 1;
spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 820318ee62c..fb83e1ffa5c 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1387,7 +1387,7 @@ ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
db_scale_line),
ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
db_scale_capture),
-ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Beep Volume", 0, 0x3c, 0, 7, 0),
ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
{
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 60cdb9e0b68..83508b3964f 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -55,7 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
* 1 = MediaForte 256-PCS
* 2 = MediaForte 256-PCPR
* 3 = MediaForte 64-PCR
- * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
+ * 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
static int tea575x_tuner[SNDRV_CARDS];
@@ -67,7 +67,10 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+
+#define TUNER_ONLY (1<<4)
+#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
/*
* Direct registers
@@ -160,7 +163,7 @@ struct fm801 {
unsigned int multichannel: 1, /* multichannel support */
secondary: 1; /* secondary codec */
unsigned char secondary_addr; /* address of the secondary codec */
- unsigned int tea575x_tuner; /* tuner flags */
+ unsigned int tea575x_tuner; /* tuner access method & flags */
unsigned short ply_ctrl; /* playback control */
unsigned short cap_ctrl; /* capture control */
@@ -1287,7 +1290,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
{
unsigned short cmdw;
- if (chip->tea575x_tuner & 0x0010)
+ if (chip->tea575x_tuner & TUNER_ONLY)
goto __ac97_ok;
/* codec cold reset + AC'97 warm reset */
@@ -1296,11 +1299,13 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
udelay(100);
outw(0, FM801_REG(chip, CODEC_CTRL));
- if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) {
- snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
- if (! resume)
- return -EIO;
- }
+ if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
+ if (!resume) {
+ snd_printk(KERN_INFO "Primary AC'97 codec not found, "
+ "assume SF64-PCR (tuner-only)\n");
+ chip->tea575x_tuner = 3 | TUNER_ONLY;
+ goto __ac97_ok;
+ }
if (chip->multichannel) {
if (chip->secondary_addr) {
@@ -1414,7 +1419,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
return err;
}
chip->port = pci_resource_start(pci, 0);
- if ((tea575x_tuner & 0x0010) == 0) {
+ if ((tea575x_tuner & TUNER_ONLY) == 0) {
if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
"FM801", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
@@ -1429,6 +1434,14 @@ static int __devinit snd_fm801_create(struct snd_card *card,
chip->multichannel = 1;
snd_fm801_chip_init(chip, 0);
+ /* init might set tuner access method */
+ tea575x_tuner = chip->tea575x_tuner;
+
+ if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
+ pci_clear_master(pci);
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ }
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_fm801_free(chip);
@@ -1438,12 +1451,13 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
#ifdef TEA575X_RADIO
- if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
+ if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+ (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
chip->tea.dev_nr = tea575x_tuner >> 16;
chip->tea.card = card;
chip->tea.freq_fixup = 10700;
chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
+ chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
snd_tea575x_init(&chip->tea);
}
#endif
@@ -1483,7 +1497,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
- if (tea575x_tuner[dev] & 0x0010)
+ if (chip->tea575x_tuner & TUNER_ONLY)
goto __fm801_tuner_only;
if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 55545e0818b..556cff937be 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
Say Y here to build a digital beep interface for HD-audio
driver. This interface is used to generate digital beeps.
+config SND_HDA_INPUT_BEEP_MODE
+ int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+ depends on SND_HDA_INPUT_BEEP=y
+ default "1"
+ range 0 2
+ help
+ Set 0 to disable the digital beep interface for HD-audio by default.
+ Set 1 to always enable the digital beep interface for HD-audio by
+ default. Set 2 to control the beep device registration to input
+ layer using a "Beep Switch" in mixer applications.
+
config SND_HDA_INPUT_JACK
bool "Support jack plugging notification via input layer"
- depends on INPUT=y || INPUT=SND_HDA_INTEL
+ depends on INPUT=y || INPUT=SND
select SND_JACK
help
Say Y here to enable the jack plugging notification via
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 3f51a981e60..5fe34a8d8c8 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+static void snd_hda_do_detach(struct hda_beep *beep)
+{
+ input_unregister_device(beep->dev);
+ beep->dev = NULL;
+ cancel_work_sync(&beep->beep_work);
+ /* turn off beep for sure */
+ snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+}
+
+static int snd_hda_do_attach(struct hda_beep *beep)
{
struct input_dev *input_dev;
- struct hda_beep *beep;
+ struct hda_codec *codec = beep->codec;
int err;
- if (!snd_hda_get_bool_hint(codec, "beep"))
- return 0; /* disabled explicitly */
-
- beep = kzalloc(sizeof(*beep), GFP_KERNEL);
- if (beep == NULL)
- return -ENOMEM;
- snprintf(beep->phys, sizeof(beep->phys),
- "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
input_dev = input_allocate_device();
if (!input_dev) {
- kfree(beep);
+ printk(KERN_INFO "hda_beep: unable to allocate input device\n");
return -ENOMEM;
}
@@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
err = input_register_device(input_dev);
if (err < 0) {
input_free_device(input_dev);
- kfree(beep);
+ printk(KERN_INFO "hda_beep: unable to register input device\n");
return err;
}
+ beep->dev = input_dev;
+ return 0;
+}
+
+static void snd_hda_do_register(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, register_work);
+
+ mutex_lock(&beep->mutex);
+ if (beep->enabled && !beep->dev)
+ snd_hda_do_attach(beep);
+ mutex_unlock(&beep->mutex);
+}
+
+static void snd_hda_do_unregister(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, unregister_work.work);
+
+ mutex_lock(&beep->mutex);
+ if (!beep->enabled && beep->dev)
+ snd_hda_do_detach(beep);
+ mutex_unlock(&beep->mutex);
+}
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+ struct hda_beep *beep = codec->beep;
+ enable = !!enable;
+ if (beep == NULL)
+ return 0;
+ if (beep->enabled != enable) {
+ beep->enabled = enable;
+ if (!enable) {
+ /* turn off beep */
+ snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+ }
+ if (beep->mode == HDA_BEEP_MODE_SWREG) {
+ if (enable) {
+ cancel_delayed_work(&beep->unregister_work);
+ schedule_work(&beep->register_work);
+ } else {
+ schedule_delayed_work(&beep->unregister_work,
+ HZ);
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ struct hda_beep *beep;
+
+ if (!snd_hda_get_bool_hint(codec, "beep"))
+ return 0; /* disabled explicitly by hints */
+ if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+ return 0; /* disabled by module option */
+
+ beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+ if (beep == NULL)
+ return -ENOMEM;
+ snprintf(beep->phys, sizeof(beep->phys),
+ "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
/* enable linear scale */
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
- beep->dev = input_dev;
beep->codec = codec;
- beep->enabled = 1;
+ beep->mode = codec->beep_mode;
codec->beep = beep;
+ INIT_WORK(&beep->register_work, &snd_hda_do_register);
+ INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+ mutex_init(&beep->mutex);
+
+ if (beep->mode == HDA_BEEP_MODE_ON) {
+ beep->enabled = 1;
+ snd_hda_do_register(&beep->register_work);
+ }
+
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
@@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
{
struct hda_beep *beep = codec->beep;
if (beep) {
- cancel_work_sync(&beep->beep_work);
-
- input_unregister_device(beep->dev);
- kfree(beep);
+ cancel_work_sync(&beep->register_work);
+ cancel_delayed_work(&beep->unregister_work);
+ if (beep->enabled)
+ snd_hda_do_detach(beep);
codec->beep = NULL;
+ kfree(beep);
}
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index 0c3de787c71..f1de1bac042 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -24,19 +24,29 @@
#include "hda_codec.h"
+#define HDA_BEEP_MODE_OFF 0
+#define HDA_BEEP_MODE_ON 1
+#define HDA_BEEP_MODE_SWREG 2
+
/* beep information */
struct hda_beep {
struct input_dev *dev;
struct hda_codec *codec;
+ unsigned int mode;
char phys[32];
int tone;
hda_nid_t nid;
unsigned int enabled:1;
+ unsigned int request_enable:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
+ struct work_struct register_work; /* registration work */
+ struct delayed_work unregister_work; /* unregistration work */
struct work_struct beep_work; /* scheduled task for beep event */
+ struct mutex mutex;
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
#else
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index af989f660cc..950ee5cfcac 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -30,6 +30,7 @@
#include <sound/tlv.h>
#include <sound/initval.h>
#include "hda_local.h"
+#include "hda_beep.h"
#include <sound/hda_hwdep.h>
/*
@@ -93,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec);
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#endif
+/**
+ * snd_hda_get_jack_location - Give a location string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
const char *snd_hda_get_jack_location(u32 cfg)
{
static char *bases[7] = {
@@ -120,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg)
}
EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+/**
+ * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
const char *snd_hda_get_jack_connectivity(u32 cfg)
{
static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
@@ -128,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
}
EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+/**
+ * snd_hda_get_jack_type - Give a type string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
const char *snd_hda_get_jack_type(u32 cfg)
{
static char *jack_types[16] = {
@@ -515,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
snd_hda_hwdep_add_sysfs(codec);
+ snd_hda_hwdep_add_power_sysfs(codec);
}
return 0;
}
@@ -820,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
return 0;
}
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
int snd_hda_codec_set_pincfg(struct hda_codec *codec,
hda_nid_t nid, unsigned int cfg)
{
@@ -827,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
-/* get the current pin config value of the given pin NID */
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;
@@ -944,7 +985,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
mutex_init(&codec->control_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 snd_kcontrol *), 32);
+ snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
if (codec->bus->modelname) {
@@ -1026,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
int snd_hda_codec_configure(struct hda_codec *codec)
{
int err;
@@ -1036,11 +1086,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
if (err < 0)
return err;
}
- /* audio codec should override the mixer name */
- if (codec->afg || !*codec->bus->card->mixername)
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
if (is_generic_config(codec)) {
err = snd_hda_parse_generic_codec(codec);
@@ -1059,6 +1104,11 @@ int snd_hda_codec_configure(struct hda_codec *codec)
patched:
if (!err && codec->patch_ops.unsol_event)
err = init_unsol_queue(codec->bus);
+ /* audio codec should override the mixer name */
+ if (!err && (codec->afg || !*codec->bus->card->mixername))
+ snprintf(codec->bus->card->mixername,
+ sizeof(codec->bus->card->mixername),
+ "%s %s", codec->vendor_name, codec->chip_name);
return err;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
@@ -1088,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
+/**
+ * snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ */
void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
{
if (!nid)
@@ -1163,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
}
-/*
- * query AMP capabilities for the given widget and direction
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
@@ -1187,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
}
EXPORT_SYMBOL_HDA(query_amp_caps);
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
@@ -1222,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
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),
@@ -1229,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ u32 pincap = snd_hda_query_pin_caps(codec, nid);
+
+ if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+
+ return snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+}
+EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status.
+ */
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ u32 sense = snd_hda_pin_sense(codec, nid);
+ return !!(sense & AC_PINSENSE_PRESENCE);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+
/*
* read the current volume to info
* if the cache exists, read the cache value.
@@ -1269,8 +1391,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
info->vol[ch] = val;
}
-/*
- * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
+/**
+ * snd_hda_codec_amp_read - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ *
+ * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
*/
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index)
@@ -1283,8 +1412,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-/*
- * update the AMP value, mask = bit mask to set, val = the value
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
*/
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val)
@@ -1303,8 +1442,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
-/*
- * update the AMP stereo with the same mask and value
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
*/
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int direction, int idx, int mask, int val)
@@ -1318,7 +1466,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
#ifdef SND_HDA_NEEDS_RESUME
-/* resume the all amp commands from the cache */
+/**
+ * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Resume the all amp commands from the cache.
+ */
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
struct hda_amp_info *buffer = codec->amp_cache.buf.list;
@@ -1344,7 +1497,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
#endif /* SND_HDA_NEEDS_RESUME */
-/* volume */
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1400,6 +1558,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
HDA_AMP_VOLMASK, val);
}
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1419,6 +1583,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1443,6 +1613,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
+/**
+ * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
@@ -1472,8 +1648,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
-/*
- * set (static) TLV for virtual master volume; recalculated as max 0dB
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
*/
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv)
@@ -1507,6 +1691,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
return snd_ctl_find_id(codec->bus->card, &id);
}
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name)
{
@@ -1514,30 +1705,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
-/* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+/**
+ * snd_hda_ctl-add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+ struct snd_kcontrol *kctl)
{
int err;
- struct snd_kcontrol **knewp;
+ struct hda_nid_item *item;
+ if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+ if (nid == 0)
+ nid = kctl->id.subdevice & 0xffff;
+ kctl->id.subdevice = 0;
+ }
err = snd_ctl_add(codec->bus->card, kctl);
if (err < 0)
return err;
- knewp = snd_array_new(&codec->mixers);
- if (!knewp)
+ item = snd_array_new(&codec->mixers);
+ if (!item)
return -ENOMEM;
- *knewp = kctl;
+ item->kctl = kctl;
+ item->nid = nid;
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
-/* Clear all controls assigned to the given codec */
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
void snd_hda_ctls_clear(struct hda_codec *codec)
{
int i;
- struct snd_kcontrol **kctls = codec->mixers.list;
+ struct hda_nid_item *items = codec->mixers.list;
for (i = 0; i < codec->mixers.used; i++)
- snd_ctl_remove(codec->bus->card, kctls[i]);
+ snd_ctl_remove(codec->bus->card, items[i].kctl);
snd_array_free(&codec->mixers);
}
@@ -1563,6 +1781,16 @@ static void hda_unlock_devices(struct snd_card *card)
spin_unlock(&card->files_lock);
}
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY. If successfully freed,
+ * returns zero.
+ */
int snd_hda_codec_reset(struct hda_codec *codec)
{
struct snd_card *card = codec->bus->card;
@@ -1626,7 +1854,22 @@ int snd_hda_codec_reset(struct hda_codec *codec)
return 0;
}
-/* create a virtual master control and add slaves */
+/**
+ * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @slaves: slave control names (optional)
+ *
+ * Create a virtual master control with the given name. The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @slaves is a NULL-terminated array of strings, each of which is a
+ * slave control name. All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves)
{
@@ -1643,7 +1886,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
@@ -1668,7 +1911,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
-/* switch */
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1682,6 +1930,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1702,6 +1956,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1733,6 +1993,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
+ *
+ * This function calls snd_hda_enable_beep_device(), which behaves differently
+ * depending on beep_mode option.
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ snd_hda_enable_beep_device(codec, *valp);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
/*
* bound volume controls
*
@@ -1742,6 +2021,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)
+/**
+ * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1759,6 +2044,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
+/**
+ * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1783,8 +2074,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
-/*
- * generic bound volume/swtich controls
+/**
+ * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
*/
int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1803,6 +2097,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
+/**
+ * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1820,6 +2120,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
+/**
+ * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1843,6 +2149,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
+/**
+ * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() macro.
+ */
int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
@@ -2126,7 +2438,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
return -ENOMEM;
kctl->id.index = idx;
kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
}
@@ -2165,14 +2477,19 @@ static struct snd_kcontrol_new spdif_share_sw = {
.put = spdif_share_sw_put,
};
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
struct hda_multi_out *mout)
{
if (!mout->dig_out_nid)
return 0;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec,
- snd_ctl_new1(&spdif_share_sw, mout));
+ return snd_hda_ctl_add(codec, mout->dig_out_nid,
+ snd_ctl_new1(&spdif_share_sw, mout));
}
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
@@ -2276,7 +2593,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
if (!kctl)
return -ENOMEM;
kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
}
@@ -2332,7 +2649,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
-/* resume the all commands from the cache */
+/**
+ * snd_hda_codec_resume_cache - Resume the all commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Execute all verbs recorded in the command caches to resume.
+ */
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
@@ -2452,9 +2774,11 @@ 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);
codec->power_on = 0;
codec->power_transition = 0;
+ codec->power_jiffies = jiffies;
#endif
}
@@ -2756,8 +3080,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
}
/**
- * snd_hda_is_supported_format - check whether the given node supports
- * the format val
+ * snd_hda_is_supported_format - Check the validity of the format
+ * @codec: HD-audio codec
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
*
* Returns 1 if supported, 0 if not.
*/
@@ -2877,51 +3205,36 @@ static int set_pcm_default_values(struct hda_codec *codec,
return 0;
}
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+ "Audio", "SPDIF", "HDMI", "Modem"
+};
+
/*
* get the empty PCM device number to assign
*/
static int get_empty_pcm_device(struct hda_bus *bus, int type)
{
- static const char *dev_name[HDA_PCM_NTYPES] = {
- "Audio", "SPDIF", "HDMI", "Modem"
- };
- /* starting device index for each PCM type */
- static int dev_idx[HDA_PCM_NTYPES] = {
- [HDA_PCM_TYPE_AUDIO] = 0,
- [HDA_PCM_TYPE_SPDIF] = 1,
- [HDA_PCM_TYPE_HDMI] = 3,
- [HDA_PCM_TYPE_MODEM] = 6
+ /* audio device indices; not linear to keep compatibility */
+ static int audio_idx[HDA_PCM_NTYPES][5] = {
+ [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+ [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+ [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },
+ [HDA_PCM_TYPE_MODEM] = { 6, -1 },
};
- /* normal audio device indices; not linear to keep compatibility */
- static int audio_idx[4] = { 0, 2, 4, 5 };
- int i, dev;
-
- switch (type) {
- case HDA_PCM_TYPE_AUDIO:
- for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
- dev = audio_idx[i];
- if (!test_bit(dev, bus->pcm_dev_bits))
- goto ok;
- }
- snd_printk(KERN_WARNING "Too many audio devices\n");
- return -EAGAIN;
- case HDA_PCM_TYPE_SPDIF:
- case HDA_PCM_TYPE_HDMI:
- case HDA_PCM_TYPE_MODEM:
- dev = dev_idx[type];
- if (test_bit(dev, bus->pcm_dev_bits)) {
- snd_printk(KERN_WARNING "%s already defined\n",
- dev_name[type]);
- return -EAGAIN;
- }
- break;
- default:
+ int i;
+
+ if (type >= HDA_PCM_NTYPES) {
snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
return -EINVAL;
}
- ok:
- set_bit(dev, bus->pcm_dev_bits);
- return dev;
+
+ for (i = 0; audio_idx[type][i] >= 0 ; i++)
+ if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+ return audio_idx[type][i];
+
+ snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+ return -EAGAIN;
}
/*
@@ -3159,14 +3472,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
*/
int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
{
- int err;
+ int err;
for (; knew->name; knew++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0) {
if (!codec->addr)
return err;
@@ -3174,7 +3487,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
if (!kctl)
return -ENOMEM;
kctl->id.device = codec->addr;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
@@ -3207,8 +3520,27 @@ static void hda_keep_power_on(struct hda_codec *codec)
{
codec->power_count++;
codec->power_on = 1;
+ codec->power_jiffies = jiffies;
}
+/* update the power on/off account with the current jiffies */
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+ unsigned long delta = jiffies - codec->power_jiffies;
+ if (codec->power_on)
+ codec->power_on_acct += delta;
+ else
+ codec->power_off_acct += delta;
+ codec->power_jiffies += delta;
+}
+
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */
void snd_hda_power_up(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
@@ -3217,7 +3549,9 @@ void snd_hda_power_up(struct hda_codec *codec)
if (codec->power_on || codec->power_transition)
return;
+ snd_hda_update_power_acct(codec);
codec->power_on = 1;
+ codec->power_jiffies = jiffies;
if (bus->ops.pm_notify)
bus->ops.pm_notify(bus);
hda_call_codec_resume(codec);
@@ -3229,9 +3563,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
#define power_save(codec) \
((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-#define power_save(codec) \
- ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */
void snd_hda_power_down(struct hda_codec *codec)
{
--codec->power_count;
@@ -3245,6 +3583,19 @@ void snd_hda_power_down(struct hda_codec *codec)
}
EXPORT_SYMBOL_HDA(snd_hda_power_down);
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list. If it's in the list,
+ * check the current AMP status, and update the the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid)
@@ -3286,6 +3637,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
/*
* Channel mode helper
*/
+
+/**
+ * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_info(struct hda_codec *codec,
struct snd_ctl_elem_info *uinfo,
const struct hda_channel_mode *chmode,
@@ -3302,6 +3657,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
+/**
+ * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_get(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
@@ -3320,6 +3678,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
+/**
+ * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
+ */
int snd_hda_ch_mode_put(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
@@ -3344,6 +3705,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
/*
* input MUX helper
*/
+
+/**
+ * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ */
int snd_hda_input_mux_info(const struct hda_input_mux *imux,
struct snd_ctl_elem_info *uinfo)
{
@@ -3362,6 +3727,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
}
EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
+/**
+ * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ */
int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol,
@@ -3421,8 +3789,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
}
}
-/*
- * open the digital out in the exclusive mode
+/**
+ * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+ struct hda_codec *codec;
+
+ if (!bus)
+ return;
+ list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (!codec->power_on)
+ continue;
+#endif
+ if (codec->patch_ops.reboot_notify)
+ codec->patch_ops.reboot_notify(codec);
+ }
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
*/
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3437,6 +3826,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ */
int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
unsigned int stream_tag,
@@ -3450,6 +3842,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ */
int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
@@ -3460,8 +3855,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
-/*
- * release the digital out
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
*/
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3473,8 +3868,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
-/*
- * set up more restrictions for analog out
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as slave, open the digital
+ * outputs, too.
*/
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -3519,9 +3918,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
*/
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -3578,8 +3979,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
-/*
- * clean up the setting for analog out
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
*/
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
@@ -3965,8 +4366,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
* generic arrays
*/
-/* get a new element from the given array
- * if it exceeds the pre-allocated array size, re-allocate the array
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array. If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
*/
void *snd_array_new(struct snd_array *array)
{
@@ -3990,7 +4397,10 @@ void *snd_array_new(struct snd_array *array)
}
EXPORT_SYMBOL_HDA(snd_array_new);
-/* free the given array elements */
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
void snd_array_free(struct snd_array *array)
{
kfree(array->list);
@@ -4000,7 +4410,12 @@ void snd_array_free(struct snd_array *array)
}
EXPORT_SYMBOL_HDA(snd_array_free);
-/*
+/**
+ * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
* used by hda_proc.c and hda_eld.c
*/
void snd_print_pcm_rates(int pcm, char *buf, int buflen)
@@ -4019,6 +4434,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
}
EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
void snd_print_pcm_bits(int pcm, char *buf, int buflen)
{
static unsigned int bits[] = { 8, 16, 20, 24, 32 };
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 99552fb5f75..1d541b7f554 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -255,9 +255,13 @@ enum {
* in HD-audio specification
*/
#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
+#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
+ * coexist with AC_PINCAP_HDMI
+ */
#define AC_PINCAP_VREF (0x37<<8)
#define AC_PINCAP_VREF_SHIFT 8
#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
+#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
/* Vref status (used in pin cap) */
#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
@@ -286,6 +290,10 @@ enum {
#define AC_PWRST_D1SUP (1<<1)
#define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3)
+#define AC_PWRST_D3COLDSUP (1<<4)
+#define AC_PWRST_S3D3COLDSUP (1<<29)
+#define AC_PWRST_CLKSTOP (1<<30)
+#define AC_PWRST_EPSS (1U<<31)
/* Power state values */
#define AC_PWRST_SETTING (0xf<<0)
@@ -631,6 +639,7 @@ struct hda_bus {
unsigned int rirb_error:1; /* error in codec communication */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
+ unsigned int power_keep_link_on:1; /* don't power off HDA link */
};
/*
@@ -674,6 +683,7 @@ struct hda_codec_ops {
#ifdef CONFIG_SND_HDA_POWER_SAVE
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
+ void (*reboot_notify)(struct hda_codec *codec);
};
/* record for amp information cache */
@@ -771,6 +781,7 @@ struct hda_codec {
/* beep device */
struct hda_beep *beep;
+ unsigned int beep_mode;
/* widget capabilities cache */
unsigned int num_nodes;
@@ -811,6 +822,9 @@ struct hda_codec {
unsigned int power_transition :1; /* 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;
#endif
/* codec-specific additional proc output */
@@ -910,6 +924,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+void snd_hda_bus_reboot_notify(struct hda_bus *bus);
/*
* power management
@@ -933,6 +948,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
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) {}
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 9446a5abea1..4228f2fe595 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -309,17 +309,12 @@ out_fail:
return -EINVAL;
}
-static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
-}
-
static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
{
int eldv;
int present;
- present = hdmi_present_sense(codec, nid);
+ present = snd_hda_pin_sense(codec, nid);
eldv = (present & AC_PINSENSE_ELDV);
present = (present & AC_PINSENSE_PRESENCE);
@@ -477,6 +472,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved"
};
+ snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
+ snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]);
@@ -518,7 +515,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
* monitor_name manufacture_id product_id
* eld_version edid_version
*/
- if (!strcmp(name, "connection_type"))
+ if (!strcmp(name, "monitor_present"))
+ e->monitor_present = val;
+ else if (!strcmp(name, "eld_valid"))
+ e->eld_valid = val;
+ else if (!strcmp(name, "connection_type"))
e->conn_type = val;
else if (!strcmp(name, "port_id"))
e->port_id = val;
@@ -560,13 +561,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
}
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+ int index)
{
char name[32];
struct snd_info_entry *entry;
int err;
- snprintf(name, sizeof(name), "eld#%d", codec->addr);
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
err = snd_card_proc_new(codec->bus->card, name, &entry);
if (err < 0)
return err;
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b36f6c5a92d..092c6a7c2ff 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_INPUT, index);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
created = 1;
@@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec)
}
/* create input MUX if multiple sources are available */
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+ err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
if (err < 0)
return err;
@@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec)
HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index,
HDA_INPUT);
- err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ err = snd_hda_ctl_add(codec, adc_node->nid,
+ snd_ctl_new1(&knew, codec));
if (err < 0)
return err;
}
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index cc24e6721d7..40ccb419b6e 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -24,6 +24,7 @@
#include <linux/compat.h>
#include <linux/mutex.h>
#include <linux/ctype.h>
+#include <linux/string.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include "hda_codec.h"
@@ -154,6 +155,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
return 0;
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static ssize_t power_on_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ snd_hda_update_power_acct(codec);
+ return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static struct device_attribute power_attrs[] = {
+ __ATTR_RO(power_on_acct),
+ __ATTR_RO(power_off_acct),
+};
+
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+ struct snd_hwdep *hwdep = codec->hwdep;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
+ snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+ hwdep->device, &power_attrs[i]);
+ return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
#ifdef CONFIG_SND_HDA_RECONFIG
/*
@@ -390,8 +429,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
char *key, *val;
struct hda_hint *hint;
- while (isspace(*buf))
- buf++;
+ buf = skip_spaces(buf);
if (!*buf || *buf == '#' || *buf == '\n')
return 0;
if (*buf == '=')
@@ -406,8 +444,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
return -EINVAL;
}
*val++ = 0;
- while (isspace(*val))
- val++;
+ val = skip_spaces(val);
remove_trail_spaces(key);
remove_trail_spaces(val);
hint = get_hint(codec, key);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index e340792f6cb..ff8ad46cc50 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -60,10 +60,14 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
static int single_cmd;
-static int enable_msi;
+static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
static char *patch[SNDRV_CARDS];
#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+ CONFIG_SND_HDA_INPUT_BEEP_MODE};
+#endif
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
module_param_array(patch, charp, NULL, 0444);
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+module_param_array(beep_mode, int, NULL, 0444);
+MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
+ "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@@ -404,6 +413,7 @@ struct azx {
unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus;
+ unsigned int beep_mode;
/* CORB/RIRB */
struct azx_rb corb;
@@ -677,6 +687,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
}
}
+ if (!chip->polling_mode) {
+ snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
+ "switching to polling mode: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
+ chip->polling_mode = 1;
+ goto again;
+ }
+
if (chip->msi) {
snd_printk(KERN_WARNING SFX "No response from codec, "
"disabling MSI: last cmd=0x%08x\n",
@@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
goto again;
}
- if (!chip->polling_mode) {
- snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
- "switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->polling_mode = 1;
- goto again;
- }
-
if (chip->probing) {
/* If this critical timeout happens during the codec probing
* phase, this is likely an access to a non-existing codec
@@ -722,9 +732,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
chip->last_cmd[addr]);
chip->single_cmd = 1;
bus->response_reset = 0;
- /* re-initialize CORB/RIRB */
+ /* release CORB/RIRB */
azx_free_cmd_io(chip);
- azx_init_cmd_io(chip);
+ /* disable unsolicited responses */
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
return -1;
}
@@ -865,7 +876,9 @@ static int azx_reset(struct azx *chip)
}
/* Accept unsolicited responses */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
+ if (!chip->single_cmd)
+ azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
+ ICH6_GCTL_UNSOL);
/* detect codecs */
if (!chip->codec_mask) {
@@ -980,7 +993,8 @@ static void azx_init_chip(struct azx *chip)
azx_int_enable(chip);
/* initialize the codec command I/O */
- azx_init_cmd_io(chip);
+ if (!chip->single_cmd)
+ azx_init_cmd_io(chip);
/* program the position buffer */
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@@ -1400,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
continue;
+ codec->beep_mode = chip->beep_mode;
codecs++;
}
}
@@ -2067,7 +2082,8 @@ static void azx_power_notify(struct hda_bus *bus)
}
if (power_on)
azx_init_chip(chip);
- else if (chip->running && power_save_controller)
+ else if (chip->running && power_save_controller &&
+ !bus->power_keep_link_on)
azx_stop_chip(chip);
}
#endif /* CONFIG_SND_HDA_POWER_SAVE */
@@ -2150,6 +2166,7 @@ static int azx_resume(struct pci_dev *pci)
static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
{
struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+ snd_hda_bus_reboot_notify(chip->bus);
azx_stop_chip(chip);
return NOTIFY_OK;
}
@@ -2217,7 +2234,9 @@ static int azx_dev_free(struct snd_device *device)
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
{}
};
@@ -2300,11 +2319,10 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
}
/*
- * white-list for enable_msi
+ * white/black-list for enable_msi
*/
-static struct snd_pci_quirk msi_white_list[] __devinitdata = {
- SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
- SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+static struct snd_pci_quirk msi_black_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
{}
};
@@ -2312,10 +2330,12 @@ static void __devinit check_msi(struct azx *chip)
{
const struct snd_pci_quirk *q;
- chip->msi = enable_msi;
- if (chip->msi)
+ if (enable_msi >= 0) {
+ chip->msi = !!enable_msi;
return;
- q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+ }
+ chip->msi = 1; /* enable MSI as default */
+ q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
if (q) {
printk(KERN_INFO
"hda_intel: msi for device %04x:%04x set to %d\n",
@@ -2432,6 +2452,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
}
}
+ /* disable 64bit DMA address for Teradici */
+ /* it does not work with device 6549:1200 subsys e4a2:040b */
+ if (chip->driver_type == AZX_DRIVER_TERA)
+ gcap &= ~ICH6_GCAP_64OK;
+
/* allow 64bit DMA address if supported by H/W */
if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
@@ -2574,6 +2599,10 @@ static int __devinit azx_probe(struct pci_dev *pci,
goto out_free;
card->private_data = chip;
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ chip->beep_mode = beep_mode[dev];
+#endif
+
/* create codec instances */
err = azx_codec_create(chip, model[dev]);
if (err < 0)
@@ -2685,6 +2714,9 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5f1dcc59002..5778ae882b8 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -23,6 +23,15 @@
#ifndef __SOUND_HDA_LOCAL_H
#define __SOUND_HDA_LOCAL_H
+/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
+ * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
+ * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
+ *
+ * Note that the subdevice field is cleared again before the real registration
+ * in snd_hda_ctl_add(), so that this value won't appear in the outside.
+ */
+#define HDA_SUBDEV_NID_FLAG (1U << 31)
+
/*
* for mixer controls
*/
@@ -33,6 +42,7 @@
/* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
@@ -53,6 +63,7 @@
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
.info = snd_hda_mixer_amp_switch_info, \
.get = snd_hda_mixer_amp_switch_get, \
.put = snd_hda_mixer_amp_switch_put, \
@@ -66,6 +77,28 @@
/* stereo mute switch */
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = snd_hda_mixer_amp_switch_put_beep, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#else
+/* no digital beep - just the standard one */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
+ HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+/* special beep mono mute switch */
+#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
+ HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* special beep stereo mute switch */
+#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
+ HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
+
+extern const char *snd_hda_pcm_type_name[];
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
@@ -81,6 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+#endif
/* lowlevel accessor with caching; use carefully */
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index);
@@ -424,8 +461,16 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+struct hda_nid_item {
+ struct snd_kcontrol *kctl;
+ hda_nid_t nid;
+};
+
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+ struct snd_kcontrol *kctl);
void snd_hda_ctls_clear(struct hda_codec *codec);
/*
@@ -437,6 +482,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
+#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SND_HDA_RECONFIG
int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
#else
@@ -490,7 +544,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
* AMP control callbacks
*/
/* retrieve parameters from private_value */
-#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
+#define get_amp_nid_(pv) ((pv) & 0xffff)
+#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
@@ -516,9 +571,11 @@ struct cea_sad {
* ELD: EDID Like Data
*/
struct hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
int eld_size;
int baseline_len;
- int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
+ int eld_ver;
int cea_edid_ver;
char monitor_name[ELD_MAX_MNL + 1];
int manufacture_id;
@@ -541,11 +598,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
void snd_hdmi_show_eld(struct hdmi_eld *eld);
#ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+ int index);
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
#else
static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
- struct hdmi_eld *eld)
+ struct hdmi_eld *eld,
+ int index)
{
return 0;
}
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 95f24e4729f..c9afc04adac 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -26,6 +26,21 @@
#include "hda_codec.h"
#include "hda_local.h"
+static char *bits_names(unsigned int bits, char *names[], int size)
+{
+ int i, n;
+ static char buf[128];
+
+ for (i = 0, n = 0; i < size; i++) {
+ if (bits & (1U<<i) && names[i])
+ n += snprintf(buf + n, sizeof(buf) - n, " %s",
+ names[i]);
+ }
+ buf[n] = '\0';
+
+ return buf;
+}
+
static const char *get_wid_type_name(unsigned int wid_value)
{
static char *names[16] = {
@@ -46,6 +61,41 @@ static const char *get_wid_type_name(unsigned int wid_value)
return "UNKNOWN Widget";
}
+static void print_nid_mixers(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i;
+ struct hda_nid_item *items = codec->mixers.list;
+ struct snd_kcontrol *kctl;
+ for (i = 0; i < codec->mixers.used; i++) {
+ if (items[i].nid == nid) {
+ kctl = items[i].kctl;
+ snd_iprintf(buffer,
+ " Control: name=\"%s\", index=%i, device=%i\n",
+ kctl->id.name, kctl->id.index, kctl->id.device);
+ }
+ }
+}
+
+static void print_nid_pcms(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int pcm, type;
+ struct hda_pcm *cpcm;
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ cpcm = &codec->pcm_info[pcm];
+ for (type = 0; type < 2; type++) {
+ if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+ continue;
+ snd_iprintf(buffer, " Device: name=\"%s\", "
+ "type=\"%s\", device=%i\n",
+ cpcm->name,
+ snd_hda_pcm_type_name[cpcm->pcm_type],
+ cpcm->pcm->device);
+ }
+ }
+}
+
static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
@@ -190,9 +240,14 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
/* Realtek uses this bit as a different meaning */
if ((codec->vendor_id >> 16) == 0x10ec)
snd_iprintf(buffer, " R/L");
- else
+ else {
+ if (caps & AC_PINCAP_HBR)
+ snd_iprintf(buffer, " HBR");
snd_iprintf(buffer, " HDMI");
+ }
}
+ if (caps & AC_PINCAP_DP)
+ snd_iprintf(buffer, " DP");
if (caps & AC_PINCAP_TRIG_REQ)
snd_iprintf(buffer, " Trigger");
if (caps & AC_PINCAP_IMP_SENSE)
@@ -363,8 +418,24 @@ static const char *get_pwr_state(u32 state)
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
+ static char *names[] = {
+ [ilog2(AC_PWRST_D0SUP)] = "D0",
+ [ilog2(AC_PWRST_D1SUP)] = "D1",
+ [ilog2(AC_PWRST_D2SUP)] = "D2",
+ [ilog2(AC_PWRST_D3SUP)] = "D3",
+ [ilog2(AC_PWRST_D3COLDSUP)] = "D3cold",
+ [ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold",
+ [ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP",
+ [ilog2(AC_PWRST_EPSS)] = "EPSS",
+ };
+
+ int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
+ if (sup)
+ snd_iprintf(buffer, " Power states: %s\n",
+ bits_names(sup, names, ARRAY_SIZE(names)));
+
snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
@@ -457,6 +528,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
(data & (1<<i)) ? 1 : 0,
(unsol & (1<<i)) ? 1 : 0);
/* FIXME: add GPO and GPI pin information */
+ print_nid_mixers(buffer, codec, nid);
}
static void print_codec_info(struct snd_info_entry *entry,
@@ -536,6 +608,9 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " CP");
snd_iprintf(buffer, "\n");
+ print_nid_mixers(buffer, codec, nid);
+ print_nid_pcms(buffer, codec, nid);
+
/* volume knob is a special widget that always have connection
* list
*/
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 2d603f6aba6..1a36137e13e 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -72,7 +72,8 @@ struct ad198x_spec {
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
unsigned int jack_present :1;
- unsigned int inv_jack_detect:1;
+ unsigned int inv_jack_detect:1; /* inverted jack-detection */
+ unsigned int inv_eapd:1; /* inverted EAPD implementation */
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
@@ -156,15 +157,19 @@ static const char *ad_slave_sws[] = {
static void ad198x_free_kctls(struct hda_codec *codec);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new ad_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
{ } /* end */
};
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
static int ad198x_build_controls(struct hda_codec *codec)
{
@@ -194,6 +199,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
}
/* create beep controls if needed */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
if (spec->beep_amp) {
struct snd_kcontrol_new *knew;
for (knew = ad_beep_mixer; knew->name; knew++) {
@@ -202,11 +208,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec,
+ get_amp_nid_(spec->beep_amp),
+ kctl);
if (err < 0)
return err;
}
}
+#endif
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
@@ -450,7 +459,7 @@ static struct hda_codec_ops ad198x_patch_ops = {
/*
* EAPD control
- * the private value = nid | (invert << 8)
+ * the private value = nid
*/
#define ad198x_eapd_info snd_ctl_boolean_mono_info
@@ -459,8 +468,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
- if (invert)
+ if (spec->inv_eapd)
ucontrol->value.integer.value[0] = ! spec->cur_eapd;
else
ucontrol->value.integer.value[0] = spec->cur_eapd;
@@ -472,11 +480,10 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
eapd = !!ucontrol->value.integer.value[0];
- if (invert)
+ if (spec->inv_eapd)
eapd = !eapd;
if (eapd == spec->cur_eapd)
return 0;
@@ -697,7 +704,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
.info = ad198x_eapd_info,
.get = ad198x_eapd_get,
.put = ad198x_eapd_put,
- .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+ .private_value = 0x1b, /* port-D */
},
{ } /* end */
};
@@ -712,10 +719,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
static void ad1986a_automic(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+ present = snd_hda_jack_detect(codec, 0x1f);
/* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+ present ? 0 : 2);
}
#define AD1986A_MIC_EVENT 0x36
@@ -754,10 +761,8 @@ static void ad1986a_update_hp(struct hda_codec *codec)
static void ad1986a_hp_automute(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(present & 0x80000000);
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
if (spec->inv_jack_detect)
spec->jack_present = !spec->jack_present;
ad1986a_update_hp(codec);
@@ -1068,6 +1073,7 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->loopback.amplist = ad1986a_loopbacks;
#endif
spec->vmaster_nid = 0x1b;
+ spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
codec->patch_ops = ad198x_patch_ops;
@@ -1547,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x06, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x06);
snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -1568,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x08, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x08);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -1785,6 +1789,14 @@ static int patch_ad1981(struct hda_codec *codec)
codec->patch_ops.init = ad1981_hp_init;
codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
+ /* set the upper-limit for mixer amp to 0dB for avoiding the
+ * possible damage by overloading
+ */
+ snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
break;
case AD1981_THINKPAD:
spec->mixers[0] = ad1981_thinkpad_mixers;
@@ -2120,7 +2132,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
.info = ad198x_eapd_info,
.get = ad198x_eapd_get,
.put = ad198x_eapd_put,
- .private_value = 0x12 | (1 << 8), /* port-D, inversed */
+ .private_value = 0x12, /* port-D */
},
{ } /* end */
@@ -2524,7 +2536,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
{
if ((res >> 26) != AD1988_HP_EVENT)
return;
- if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+ if (snd_hda_jack_detect(codec, 0x11))
snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
else
snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
@@ -2569,6 +2581,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
@@ -3059,6 +3073,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->input_mux = &ad1988_laptop_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1988_laptop_mixers;
+ spec->inv_eapd = 1; /* inverted EAPD */
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1988_laptop_init_verbs;
if (board_config == AD1988_LAPTOP_DIG)
@@ -3768,8 +3783,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x11);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3781,8 +3795,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x14);
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
present ? 0 : 1);
}
@@ -3817,13 +3830,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
- if (!present) {
- present = snd_hda_codec_read(codec, 0x12, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
- }
+ present = snd_hda_jack_detect(codec, 0x11);
+ if (!present)
+ present = snd_hda_jack_detect(codec, 0x12);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3835,11 +3844,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec)
{
unsigned int idx;
- if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE)
+ if (snd_hda_jack_detect(codec, 0x14))
idx = 0;
- else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE)
+ else if (snd_hda_jack_detect(codec, 0x1c))
idx = 4;
else
idx = 1;
@@ -4008,8 +4015,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x11);
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -4117,14 +4123,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
/* switch to external mic if plugged */
static void ad1984a_touchsmart_automic(struct hda_codec *codec)
{
- if (snd_hda_codec_read(codec, 0x1c, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
+ if (snd_hda_jack_detect(codec, 0x1c))
snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x4);
- } else {
+ else
snd_hda_codec_write(codec, 0x0c, 0,
AC_VERB_SET_CONNECT_SEL, 0x5);
- }
}
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index d08353d3bb7..af478019088 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
@@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 8ba306856d3..fe0423c3959 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -66,6 +66,7 @@ struct cs_spec {
/* available models */
enum {
CS420X_MBP55,
+ CS420X_IMAC27,
CS420X_AUTO,
CS420X_MODELS
};
@@ -500,7 +501,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec);
- return snd_hda_ctl_add(codec, *kctlp);
+ return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
}
static int add_volume(struct hda_codec *codec, const char *name,
@@ -513,7 +514,7 @@ static int add_volume(struct hda_codec *codec, const char *name,
knew.private_value = pval;
snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
*kctlp = snd_ctl_new1(&knew, codec);
- return snd_hda_ctl_add(codec, *kctlp);
+ return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
}
static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
@@ -536,14 +537,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
spec->vmaster_sw =
snd_ctl_make_virtual_master("Master Playback Switch", NULL);
- err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
if (err < 0)
return err;
snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
spec->vmaster_vol =
snd_ctl_make_virtual_master("Master Playback Volume", tlv);
- err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
if (err < 0)
return err;
return 0;
@@ -756,13 +757,13 @@ static int build_input(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = (long)spec->capture_bind[i];
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
}
if (spec->num_inputs > 1 && !spec->mic_detect) {
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&cs_capture_source, codec));
if (err < 0)
return err;
@@ -807,7 +808,7 @@ static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps, present, hp_present;
+ unsigned int caps, hp_present;
hda_nid_t nid;
int i;
@@ -817,12 +818,7 @@ static void cs_automute(struct hda_codec *codec)
caps = snd_hda_query_pin_caps(codec, nid);
if (!(caps & AC_PINCAP_PRES_DETECT))
continue;
- if (caps & AC_PINCAP_TRIG_REQ)
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
+ hp_present = snd_hda_jack_detect(codec, nid);
if (hp_present)
break;
}
@@ -832,7 +828,8 @@ static void cs_automute(struct hda_codec *codec)
AC_VERB_SET_PIN_WIDGET_CONTROL,
hp_present ? 0 : PIN_OUT);
}
- if (spec->board_config == CS420X_MBP55) {
+ if (spec->board_config == CS420X_MBP55 ||
+ spec->board_config == CS420X_IMAC27) {
unsigned int gpio = hp_present ? 0x02 : 0x08;
snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, gpio);
@@ -844,15 +841,11 @@ static void cs_automic(struct hda_codec *codec)
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
- unsigned int caps, present;
+ unsigned int present;
nid = cfg->input_pins[spec->automic_idx];
- caps = snd_hda_query_pin_caps(codec, nid);
- if (caps & AC_PINCAP_TRIG_REQ)
- snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (present & AC_PINSENSE_PRESENCE)
+ present = snd_hda_jack_detect(codec, nid);
+ if (present)
change_cur_input(codec, spec->automic_idx, 0);
else {
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
@@ -947,7 +940,7 @@ static void init_input(struct hda_codec *codec)
coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
if (is_active_pin(codec, CS_DMIC1_PIN_NID))
coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0
- * No effect if SPDIF_OUT2 is slected in
+ * No effect if SPDIF_OUT2 is selected in
* IDX_SPDIF_CTL.
*/
cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
@@ -1078,12 +1071,14 @@ static int cs_parse_auto_config(struct hda_codec *codec)
static const char *cs420x_models[CS420X_MODELS] = {
[CS420X_MBP55] = "mbp55",
+ [CS420X_IMAC27] = "imac27",
[CS420X_AUTO] = "auto",
};
static struct snd_pci_quirk cs420x_cfg_tbl[] = {
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
{} /* terminator */
};
@@ -1106,8 +1101,23 @@ static struct cs_pincfg mbp55_pincfgs[] = {
{} /* terminator */
};
+static struct cs_pincfg imac27_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100140 },
+ { 0x0b, 0x90100142 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x01ab9070 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
[CS420X_MBP55] = mbp55_pincfgs,
+ [CS420X_IMAC27] = imac27_pincfgs,
};
static void fix_pincfg(struct hda_codec *codec, int model)
@@ -1137,6 +1147,7 @@ static int patch_cs420x(struct hda_codec *codec)
fix_pincfg(codec, spec->board_config);
switch (spec->board_config) {
+ case CS420X_IMAC27:
case CS420X_MBP55:
/* GPIO1 = headphones */
/* GPIO3 = speakers */
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 780e1a72114..a45c1169762 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -66,7 +66,7 @@ struct cmi_spec {
struct hda_pcm pcm_rec[2]; /* PCM information */
- /* pin deafault configuration */
+ /* pin default configuration */
hda_nid_t pin_nid[NUM_PINS];
unsigned int def_conf[NUM_PINS];
unsigned int pin_def_confs;
@@ -197,8 +197,8 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT),
{ } /* end */
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 3fbbc8c01e7..c578c28f368 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -29,6 +29,7 @@
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_beep.h"
#define CXT_PIN_DIR_IN 0x00
#define CXT_PIN_DIR_OUT 0x01
@@ -110,6 +111,8 @@ struct conexant_spec {
unsigned int dell_automute;
unsigned int port_d_mode;
+ unsigned char ext_mic_bias;
+ unsigned int dell_vostro;
};
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -396,9 +399,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
for (i = 0; i < spec->jacks.used; i++) {
if (jacks->nid == nid) {
unsigned int present;
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, nid);
present = (present) ? jacks->type : 0 ;
@@ -477,6 +478,7 @@ static void conexant_free(struct hda_codec *codec)
snd_array_free(&spec->jacks);
}
#endif
+ snd_hda_detach_beep_device(codec);
kfree(codec->spec);
}
@@ -749,8 +751,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x12, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x12);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -764,8 +765,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
unsigned int bits;
- spec->hp_present = snd_hda_codec_read(codec, 0x11, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x11);
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
@@ -1174,9 +1174,10 @@ static int patch_cxt5045(struct hda_codec *codec)
switch (codec->subsystem_id >> 16) {
case 0x103c:
- /* HP laptop has a really bad sound over 0dB on NID 0x17.
- * Fix max PCM level to 0 dB
- * (originall it has 0x2b steps with 0dB offset 0x14)
+ case 0x1734:
+ /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB
+ * on NID 0x17. Fix max PCM level to 0 dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
*/
snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
(0x14 << AC_AMPCAP_OFFSET_SHIFT) |
@@ -1242,8 +1243,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
struct conexant_spec *spec = codec->spec;
unsigned int bits;
- spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x13);
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
/* See the note in cxt5047_hp_master_sw_put */
@@ -1266,8 +1266,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
if (present)
snd_hda_sequence_write(codec, mic_jack_on);
else
@@ -1414,16 +1413,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put,
},
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -1620,9 +1610,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec)
if (spec->no_auto_mic)
return;
- present = snd_hda_codec_read(codec, 0x17, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x17);
snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_CONNECT_SEL,
present ? 0x01 : 0x00);
@@ -1637,9 +1625,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
if (spec->no_auto_mic)
return;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x18);
if (present)
spec->cur_adc_idx = 1;
else
@@ -1660,9 +1646,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
- AC_VERB_GET_PIN_SENSE, 0) &
- AC_PINSENSE_PRESENCE;
+ spec->hp_present = snd_hda_jack_detect(codec, 0x16);
cxt5051_update_speaker(codec);
}
@@ -1927,6 +1911,11 @@ static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
#define CXT5066_SPDIF_OUT 0x21
+/* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal with
+ * the DC input range of the codec. */
+#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
+
static struct hda_channel_mode cxt5066_modes[1] = {
{ 2, NULL },
};
@@ -1980,9 +1969,10 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
/* toggle input of built-in and mic jack appropriately */
static void cxt5066_automic(struct hda_codec *codec)
{
- static struct hda_verb ext_mic_present[] = {
+ struct conexant_spec *spec = codec->spec;
+ struct hda_verb ext_mic_present[] = {
/* enable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
/* switch to external mic input */
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
@@ -2004,8 +1994,47 @@ static void cxt5066_automic(struct hda_codec *codec)
};
unsigned int present;
- present = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x1a);
+ if (present) {
+ snd_printdd("CXT5066: external microphone detected\n");
+ snd_hda_sequence_write(codec, ext_mic_present);
+ } else {
+ snd_printdd("CXT5066: external microphone absent\n");
+ snd_hda_sequence_write(codec, ext_mic_absent);
+ }
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_vostro_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int present;
+
+ struct hda_verb ext_mic_present[] = {
+ /* enable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+
+ /* switch to external mic input */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* disable internal digital mic */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+ static struct hda_verb ext_mic_absent[] = {
+ /* enable internal mic, port C */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ /* switch to internal mic input */
+ {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+
+ /* disable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+
+ present = snd_hda_jack_detect(codec, 0x1a);
if (present) {
snd_printdd("CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present);
@@ -2022,12 +2051,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
unsigned int portA, portD;
/* Port A */
- portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ portA = snd_hda_jack_detect(codec, 0x19);
/* Port D */
- portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE) << 1;
+ portD = snd_hda_jack_detect(codec, 0x1c);
spec->hp_present = !!(portA | portD);
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
@@ -2049,6 +2076,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
+/* unsolicited event for jack sensing */
+static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
+{
+ snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5066_hp_automute(codec);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cxt5066_vostro_automic(codec);
+ break;
+ }
+}
+
static const struct hda_input_mux cxt5066_analog_mic_boost = {
.num_items = 5,
.items = {
@@ -2071,9 +2112,12 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int val;
+ hda_nid_t nid = kcontrol->private_value & 0xff;
+ int inout = (kcontrol->private_value & 0x100) ?
+ AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;
- val = snd_hda_codec_read(codec, 0x17, 0,
- AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT);
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE, inout);
ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
return 0;
@@ -2085,6 +2129,9 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
unsigned int idx;
+ hda_nid_t nid = kcontrol->private_value & 0xff;
+ int inout = (kcontrol->private_value & 0x100) ?
+ AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT;
if (!imux->num_items)
return 0;
@@ -2092,9 +2139,9 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
if (idx >= imux->num_items)
idx = imux->num_items - 1;
- snd_hda_codec_write_cache(codec, 0x17, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+ AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout |
imux->items[idx].index);
return 1;
@@ -2163,10 +2210,11 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Mic Boost Capture Enum",
+ .name = "Ext Mic Boost Capture Enum",
.info = cxt5066_mic_boost_mux_enum_info,
.get = cxt5066_mic_boost_mux_enum_get,
.put = cxt5066_mic_boost_mux_enum_put,
+ .private_value = 0x17,
},
HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
@@ -2174,6 +2222,19 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
{}
};
+static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Int Mic Boost Capture Enum",
+ .info = cxt5066_mic_boost_mux_enum_info,
+ .get = cxt5066_mic_boost_mux_enum_get,
+ .put = cxt5066_mic_boost_mux_enum_put,
+ .private_value = 0x23 | 0x100,
+ },
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+ {}
+};
+
static struct hda_verb cxt5066_init_verbs[] = {
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
@@ -2235,7 +2296,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS},
/* Port C: internal microphone */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -2290,6 +2351,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{ } /* end */
};
+static struct hda_verb cxt5066_init_verbs_vostro[] = {
+ /* Port A: headphones */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* Port B: external microphone */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port C: unused */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port D: unused */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port E: unused, but has primary EAPD */
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+ /* Port F: unused */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port G: internal speakers */
+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* DAC2: unused */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* Digital microphone port */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ /* Audio input selectors */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+ /* Disable SPDIF */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* enable unsolicited events for Port A and B */
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ { } /* end */
+};
+
static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ } /* end */
@@ -2298,11 +2420,16 @@ static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
/* initialize jack-sensing, too */
static int cxt5066_init(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
+
snd_printdd("CXT5066: init\n");
conexant_init(codec);
if (codec->patch_ops.unsol_event) {
cxt5066_hp_automute(codec);
- cxt5066_automic(codec);
+ if (spec->dell_vostro)
+ cxt5066_vostro_automic(codec);
+ else
+ cxt5066_automic(codec);
}
return 0;
}
@@ -2311,6 +2438,7 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
+ CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
CXT5066_MODELS
};
@@ -2318,6 +2446,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
+ [CXT5066_DELL_VOSTO] = "dell-vostro"
};
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2325,6 +2454,8 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
CXT5066_LAPTOP),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP),
+ SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+ SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
{}
};
@@ -2352,6 +2483,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->input_mux = &cxt5066_capture_source;
spec->port_d_mode = PIN_HP;
+ spec->ext_mic_bias = PIN_VREF80;
spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5066_init_verbs;
@@ -2383,6 +2515,23 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
spec->port_d_mode = 0;
+ spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS;
+
+ /* no S/PDIF out */
+ spec->multiout.dig_out_nid = 0;
+
+ /* input source automatically selected */
+ spec->input_mux = NULL;
+ break;
+ case CXT5066_DELL_VOSTO:
+ codec->patch_ops.unsol_event = cxt5066_vostro_event;
+ spec->init_verbs[0] = cxt5066_init_verbs_vostro;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+ spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
+ spec->port_d_mode = 0;
+ spec->dell_vostro = 1;
+ snd_hda_attach_beep_device(codec, 0x13);
/* no S/PDIF out */
spec->multiout.dig_out_nid = 0;
@@ -2407,6 +2556,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5051 },
{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
.patch = patch_cxt5066 },
+ { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
+ .patch = patch_cxt5066 },
{} /* terminator */
};
@@ -2414,6 +2565,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045");
MODULE_ALIAS("snd-hda-codec-id:14f15047");
MODULE_ALIAS("snd-hda-codec-id:14f15051");
MODULE_ALIAS("snd-hda-codec-id:14f15066");
+MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index 01a18ed475a..918f40378d5 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -33,15 +33,41 @@
#include "hda_codec.h"
#include "hda_local.h"
-static hda_nid_t cvt_nid; /* audio converter */
-static hda_nid_t pin_nid; /* HDMI output pin */
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define INTEL_HDMI_CVTS 2
+#define INTEL_HDMI_PINS 3
-#define INTEL_HDMI_EVENT_TAG 0x08
+static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+ "INTEL HDMI 0",
+ "INTEL HDMI 1",
+};
struct intel_hdmi_spec {
- struct hda_multi_out multiout;
- struct hda_pcm pcm_rec;
- struct hdmi_eld sink_eld;
+ int num_cvts;
+ int num_pins;
+ hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */
+ hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */
+
+ /*
+ * source connection for each pin
+ */
+ hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
+
+ /*
+ * HDMI sink attached to each pin
+ */
+ struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
+
+ /*
+ * export one pcm per pipe
+ */
+ struct hda_pcm pcm_rec[INTEL_HDMI_CVTS];
};
struct hdmi_audio_infoframe {
@@ -120,38 +146,78 @@ struct cea_channel_speaker_allocation {
};
/*
+ * ALSA sequence is:
+ *
+ * surround40 surround41 surround50 surround51 surround71
+ * ch0 front left = = = =
+ * ch1 front right = = = =
+ * ch2 rear left = = = =
+ * ch3 rear right = = = =
+ * ch4 LFE center center center
+ * ch5 LFE LFE
+ * ch6 side left
+ * ch7 side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+ /* stereo */
+ [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* 2.1 */
+ [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* Dolby Surround */
+ [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* surround40 */
+ [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+ /* 4ch */
+ [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+ /* surround41 */
+ [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+ /* surround50 */
+ [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+ /* surround51 */
+ [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+ /* 7.1 */
+ [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
* This is an ordered list!
*
* The preceding ones have better chances to be selected by
* hdmi_setup_channel_allocation().
*/
static struct cea_channel_speaker_allocation channel_allocations[] = {
-/* channel: 8 7 6 5 4 3 2 1 */
+/* channel: 7 6 5 4 3 2 1 0 */
{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
/* 2.1 */
{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
/* Dolby Surround */
{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
+ /* surround40 */
+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
+ /* surround41 */
+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
+ /* surround50 */
+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
+ /* surround51 */
+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
+ /* 6.1 */
+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
+ /* surround71 */
+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
+
{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
- /* 5.1 */
-{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
- /* 6.1 */
-{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
- /* 7.1 */
-{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
@@ -185,39 +251,195 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
};
/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+ int i;
+
+ for (i = 0; nids[i]; i++)
+ if (nids[i] == nid)
+ return i;
+
+ snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+ return -EINVAL;
+}
+
+static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+ int conn_len, curr;
+ int index;
+
+ if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+ snd_printk(KERN_WARNING
+ "HDMI: pin %d wcaps %#x "
+ "does not support connection list\n",
+ pin_nid, get_wcaps(codec, pin_nid));
+ return -EINVAL;
+ }
+
+ conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
+ HDA_MAX_CONNECTIONS);
+ if (conn_len > 1)
+ curr = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ else
+ curr = 0;
+
+ index = hda_node_index(spec->pin, pin_nid);
+ if (index < 0)
+ return -EINVAL;
+
+ spec->pin_cvt[index] = conn_list[curr];
+
+ return 0;
+}
+
+static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_eld *eld)
+{
+ if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+ snd_hdmi_show_eld(eld);
+}
+
+static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_eld *eld)
+{
+ int present = snd_hda_pin_sense(codec, pin_nid);
+
+ eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+
+ if (present & AC_PINSENSE_ELDV)
+ hdmi_get_show_eld(codec, pin_nid, eld);
+}
+
+static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ if (spec->num_pins >= INTEL_HDMI_PINS) {
+ snd_printk(KERN_WARNING
+ "HDMI: no space for pin %d \n", pin_nid);
+ return -EINVAL;
+ }
+
+ hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+
+ spec->pin[spec->num_pins] = pin_nid;
+ spec->num_pins++;
+
+ /*
+ * It is assumed that converter nodes come first in the node list and
+ * hence have been registered and usable now.
+ */
+ return intel_hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ if (spec->num_cvts >= INTEL_HDMI_CVTS) {
+ snd_printk(KERN_WARNING
+ "HDMI: no space for converter %d \n", nid);
+ return -EINVAL;
+ }
+
+ spec->cvt[spec->num_cvts] = nid;
+ spec->num_cvts++;
+
+ return 0;
+}
+
+static int intel_hdmi_parse_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+ int i, nodes;
+
+ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ if (!nid || nodes < 0) {
+ snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nodes; i++, nid++) {
+ unsigned int caps;
+ unsigned int type;
+
+ caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ switch (type) {
+ case AC_WID_AUD_OUT:
+ if (intel_hdmi_add_cvt(codec, nid) < 0)
+ return -EINVAL;
+ break;
+ case AC_WID_PIN:
+ caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ continue;
+ if (intel_hdmi_add_pin(codec, nid) < 0)
+ return -EINVAL;
+ break;
+ }
+ }
+
+ /*
+ * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
+ * can be lost and presence sense verb will become inaccurate if the
+ * HDA link is powered off at hot plug or hw initialization time.
+ */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
+ AC_PWRST_EPSS))
+ codec->bus->power_keep_link_on = 1;
+#endif
+
+ return 0;
+}
+
+/*
* HDMI routines
*/
#ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int *packet_index, int *byte_index)
{
int val;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_INDEX, 0);
*packet_index = val >> 5;
*byte_index = val & 0x1f;
}
#endif
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int packet_index, int byte_index)
{
int val;
val = (packet_index << 5) | (byte_index & 0x1f);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
}
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
unsigned char val)
{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
}
-static void hdmi_enable_output(struct hda_codec *codec)
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
{
/* Unmute */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
@@ -231,7 +453,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
/*
* Enable Audio InfoFrame Transmission
*/
-static void hdmi_start_infoframe_trans(struct hda_codec *codec)
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
{
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
@@ -241,59 +464,50 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
/*
* Disable Audio InfoFrame Transmission
*/
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
{
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
AC_DIPXMIT_DISABLE);
}
-static int hdmi_get_channel_count(struct hda_codec *codec)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
{
- return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
+ return 1 + snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
-static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+static void hdmi_set_channel_count(struct hda_codec *codec,
+ hda_nid_t nid, int chs)
{
- snd_hda_codec_write(codec, cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-
- if (chs != hdmi_get_channel_count(codec))
- snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
- chs, hdmi_get_channel_count(codec));
+ if (chs != hdmi_get_channel_count(codec, nid))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
}
-static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+static void hdmi_debug_channel_mapping(struct hda_codec *codec,
+ hda_nid_t pin_nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int i;
int slot;
for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, cvt_nid, 0,
+ slot = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_CHAN_SLOT, i);
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
- slot >> 4, slot & 0x7);
+ slot >> 4, slot & 0xf);
}
#endif
}
-static void hdmi_parse_eld(struct hda_codec *codec)
-{
- struct intel_hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld = &spec->sink_eld;
-
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
-}
-
/*
* Audio InfoFrame routines
*/
-static void hdmi_debug_dip_size(struct hda_codec *codec)
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int i;
@@ -310,7 +524,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
#endif
}
-static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
{
#ifdef BE_PARANOID
int i, j;
@@ -339,23 +553,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
#endif
}
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
- struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
{
- u8 *params = (u8 *)ai;
+ u8 *bytes = (u8 *)ai;
u8 sum = 0;
int i;
- hdmi_debug_dip_size(codec);
- hdmi_clear_dip_buffers(codec); /* be paranoid */
+ ai->checksum = 0;
+
+ for (i = 0; i < sizeof(*ai); i++)
+ sum += bytes[i];
- for (i = 0; i < sizeof(ai); i++)
- sum += params[i];
ai->checksum = - sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ struct hdmi_audio_infoframe *ai)
+{
+ u8 *bytes = (u8 *)ai;
+ int i;
+
+ hdmi_debug_dip_size(codec, pin_nid);
+ hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+ hdmi_checksum_audio_infoframe(ai);
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(ai); i++)
- hdmi_write_dip_byte(codec, pin_nid, params[i]);
+ for (i = 0; i < sizeof(*ai); i++)
+ hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
}
/*
@@ -386,11 +612,11 @@ static void init_channel_allocations(void)
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
struct hdmi_audio_infoframe *ai)
{
struct intel_hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld = &spec->sink_eld;
+ struct hdmi_eld *eld;
int i;
int spk_mask = 0;
int channels = 1 + (ai->CC02_CT47 & 0x7);
@@ -402,6 +628,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
if (channels <= 2)
return 0;
+ i = hda_node_index(spec->pin_cvt, nid);
+ if (i < 0)
+ return 0;
+ eld = &spec->sink_eld[i];
+
/*
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
* in console or for audio devices. Assume the highest speakers
@@ -440,30 +671,61 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
}
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
- struct hdmi_audio_infoframe *ai)
+ hda_nid_t pin_nid,
+ struct hdmi_audio_infoframe *ai)
{
int i;
+ int ca = ai->CA;
+ int err;
- if (!ai->CA)
- return;
-
- /*
- * TODO: adjust channel mapping if necessary
- * ALSA sequence is front/surr/clfe/side?
- */
+ if (hdmi_channel_mapping[ca][1] == 0) {
+ for (i = 0; i < channel_allocations[ca].channels; i++)
+ hdmi_channel_mapping[ca][i] = i | (i << 4);
+ for (; i < 8; i++)
+ hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
+ }
- for (i = 0; i < 8; i++)
- snd_hda_codec_write(codec, cvt_nid, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
+ for (i = 0; i < 8; i++) {
+ err = snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ hdmi_channel_mapping[ca][i]);
+ if (err) {
+ snd_printdd(KERN_INFO "HDMI: channel mapping failed\n");
+ break;
+ }
+ }
- hdmi_debug_channel_mapping(codec);
+ hdmi_debug_channel_mapping(codec, pin_nid);
}
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+ struct hdmi_audio_infoframe *ai)
+{
+ u8 *bytes = (u8 *)ai;
+ u8 val;
+ int i;
+
+ if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+ != AC_DIPXMIT_BEST)
+ return false;
+
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ for (i = 0; i < sizeof(*ai); i++) {
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_DATA, 0);
+ if (val != bytes[i])
+ return false;
+ }
+
+ return true;
+}
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
struct snd_pcm_substream *substream)
{
+ struct intel_hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid;
+ int i;
struct hdmi_audio_infoframe ai = {
.type = 0x84,
.ver = 0x01,
@@ -471,11 +733,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
.CC02_CT47 = substream->runtime->channels - 1,
};
- hdmi_setup_channel_allocation(codec, &ai);
- hdmi_setup_channel_mapping(codec, &ai);
+ hdmi_setup_channel_allocation(codec, nid, &ai);
- hdmi_fill_audio_infoframe(codec, &ai);
- hdmi_start_infoframe_trans(codec);
+ for (i = 0; i < spec->num_pins; i++) {
+ if (spec->pin_cvt[i] != nid)
+ continue;
+ if (!spec->sink_eld[i].monitor_present)
+ continue;
+
+ pin_nid = spec->pin[i];
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+ hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+ hdmi_start_infoframe_trans(codec, pin_nid);
+ }
+ }
}
@@ -485,27 +758,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
+ struct intel_hdmi_spec *spec = codec->spec;
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int pind = !!(res & AC_UNSOL_RES_PD);
int eldv = !!(res & AC_UNSOL_RES_ELDV);
+ int index;
printk(KERN_INFO
- "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
- pind, eldv);
+ "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ tag, pind, eldv);
+
+ index = hda_node_index(spec->pin, tag);
+ if (index < 0)
+ return;
+
+ spec->sink_eld[index].monitor_present = pind;
+ spec->sink_eld[index].eld_valid = eldv;
if (pind && eldv) {
- hdmi_parse_eld(codec);
+ hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
/* TODO: do real things about ELD */
}
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO
- "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ tag,
subtag,
cp_state,
cp_ready);
@@ -520,10 +805,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
+ struct intel_hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- if (tag != INTEL_HDMI_EVENT_TAG) {
+ if (hda_node_index(spec->pin, tag) < 0) {
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
@@ -538,24 +824,29 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
* Callbacks
*/
-static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag, int format)
{
- struct intel_hdmi_spec *spec = codec->spec;
-
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+ int tag;
+ int fmt;
-static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct intel_hdmi_spec *spec = codec->spec;
+ tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
+ fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
- hdmi_stop_infoframe_trans(codec);
+ snd_printdd("hdmi_setup_stream: "
+ "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
+ nid,
+ tag == stream_tag ? "" : "new-",
+ stream_tag,
+ fmt == format ? "" : "new-",
+ format);
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ if (tag != stream_tag)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
+ if (fmt != format)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
}
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -564,43 +855,53 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
unsigned int format,
struct snd_pcm_substream *substream)
{
- struct intel_hdmi_spec *spec = codec->spec;
-
- snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ hdmi_set_channel_count(codec, hinfo->nid,
+ substream->runtime->channels);
- hdmi_set_channel_count(codec, substream->runtime->channels);
+ hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
- hdmi_setup_audio_infoframe(codec, substream);
+ hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+ return 0;
+}
+static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
return 0;
}
static struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
- .channels_max = 8,
.ops = {
- .open = intel_hdmi_playback_pcm_open,
- .close = intel_hdmi_playback_pcm_close,
- .prepare = intel_hdmi_playback_pcm_prepare
+ .prepare = intel_hdmi_playback_pcm_prepare,
+ .cleanup = intel_hdmi_playback_pcm_cleanup,
},
};
static int intel_hdmi_build_pcms(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
- codec->num_pcms = 1;
+ codec->num_pcms = spec->num_cvts;
codec->pcm_info = info;
- /* NID to query formats and rates and setup streams */
- intel_hdmi_pcm_playback.nid = cvt_nid;
+ for (i = 0; i < codec->num_pcms; i++, info++) {
+ unsigned int chans;
+
+ chans = get_wcaps(codec, spec->cvt[i]);
+ chans = get_wcaps_channels(chans);
- info->name = "INTEL HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+ info->name = intel_hdmi_pcm_names[i];
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ intel_hdmi_pcm_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+ }
return 0;
}
@@ -609,29 +910,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
int err;
+ int i;
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
+ for (i = 0; i < codec->num_pcms; i++) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+ if (err < 0)
+ return err;
+ }
return 0;
}
static int intel_hdmi_init(struct hda_codec *codec)
{
- hdmi_enable_output(codec);
+ struct intel_hdmi_spec *spec = codec->spec;
+ int i;
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
+ for (i = 0; spec->pin[i]; i++) {
+ hdmi_enable_output(codec, spec->pin[i]);
+ snd_hda_codec_write(codec, spec->pin[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | spec->pin[i]);
+ }
return 0;
}
static void intel_hdmi_free(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
- snd_hda_eld_proc_free(codec, &spec->sink_eld);
kfree(spec);
}
@@ -643,49 +954,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
.unsol_event = intel_hdmi_unsol_event,
};
-static int do_patch_intel_hdmi(struct hda_codec *codec)
+static int patch_intel_hdmi(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec;
+ int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 8;
- spec->multiout.dig_out_nid = cvt_nid;
-
codec->spec = spec;
+ if (intel_hdmi_parse_codec(codec) < 0) {
+ codec->spec = NULL;
+ kfree(spec);
+ return -EINVAL;
+ }
codec->patch_ops = intel_hdmi_patch_ops;
- snd_hda_eld_proc_new(codec, &spec->sink_eld);
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
init_channel_allocations();
return 0;
}
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
- cvt_nid = 0x02;
- pin_nid = 0x03;
- return do_patch_intel_hdmi(codec);
-}
-
-static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
-{
- cvt_nid = 0x02;
- pin_nid = 0x04;
- return do_patch_intel_hdmi(codec);
-}
-
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
- { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
+ { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index 9fb60276f5c..6afdab09bab 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -397,6 +397,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+ { .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
@@ -406,6 +407,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
MODULE_ALIAS("snd-hda-codec-id:10de0002");
MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
MODULE_ALIAS("snd-hda-codec-id:10de0006");
MODULE_ALIAS("snd-hda-codec-id:10de0007");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ff20048504b..c7465053d6b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -131,8 +131,8 @@ enum {
enum {
ALC269_BASIC,
ALC269_QUANTA_FL1,
- ALC269_ASUS_EEEPC_P703,
- ALC269_ASUS_EEEPC_P901,
+ ALC269_ASUS_AMIC,
+ ALC269_ASUS_DMIC,
ALC269_FUJITSU,
ALC269_LIFEBOOK,
ALC269_AUTO,
@@ -188,6 +188,8 @@ enum {
ALC663_ASUS_MODE4,
ALC663_ASUS_MODE5,
ALC663_ASUS_MODE6,
+ ALC663_ASUS_MODE7,
+ ALC663_ASUS_MODE8,
ALC272_DELL,
ALC272_DELL_ZM1,
ALC272_SAMSUNG_NC10,
@@ -208,6 +210,7 @@ enum {
ALC885_MBP3,
ALC885_MB5,
ALC885_IMAC24,
+ ALC885_IMAC91,
ALC883_3ST_2ch_DIG,
ALC883_3ST_6ch_DIG,
ALC883_3ST_6ch,
@@ -334,6 +337,9 @@ struct alc_spec {
/* hooks */
void (*init_hook)(struct hda_codec *codec);
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ void (*power_hook)(struct hda_codec *codec, int power);
+#endif
/* for pin sensing */
unsigned int sense_updated: 1;
@@ -385,6 +391,7 @@ struct alc_config_preset {
void (*init_hook)(struct hda_codec *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_amp_list *loopbacks;
+ void (*power_hook)(struct hda_codec *codec, int power);
#endif
};
@@ -897,6 +904,7 @@ static void setup_preset(struct hda_codec *codec,
spec->unsol_event = preset->unsol_event;
spec->init_hook = preset->init_hook;
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->power_hook = preset->power_hook;
spec->loopback.amplist = preset->loopbacks;
#endif
@@ -961,18 +969,12 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
static void alc_automute_pin(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present, pincap;
unsigned int nid = spec->autocfg.hp_pins[0];
int i;
if (!nid)
return;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, nid);
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
if (!nid)
@@ -1012,9 +1014,7 @@ static void alc_mic_automute(struct hda_codec *codec)
cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
- present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- present &= AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
if (present) {
alive = &spec->ext_mic;
dead = &spec->int_mic;
@@ -1402,6 +1402,17 @@ static void alc_pick_fixup(struct hda_codec *codec,
add_verb(codec->spec, fix->verbs);
}
+static int alc_read_coef_idx(struct hda_codec *codec,
+ unsigned int coef_idx)
+{
+ unsigned int val;
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ coef_idx);
+ val = snd_hda_codec_read(codec, 0x20, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+ return val;
+}
+
/*
* ALC888
*/
@@ -1513,7 +1524,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
static void alc_automute_amp(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int val, mute, pincap;
+ unsigned int mute;
hda_nid_t nid;
int i;
@@ -1522,13 +1533,7 @@ static void alc_automute_amp(struct hda_codec *codec)
nid = spec->autocfg.hp_pins[i];
if (!nid)
break;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (val & AC_PINSENSE_PRESENCE) {
+ if (snd_hda_jack_detect(codec, nid)) {
spec->jack_present = 1;
break;
}
@@ -1665,9 +1670,6 @@ static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
/* some bit here disables the other DACs. Init=0x4900 */
{0x20, AC_VERB_SET_COEF_INDEX, 0x08},
{0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* Enable amplifiers */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
/* DMIC fix
* This laptop has a stereo digital microphone. The mics are only 1cm apart
* which makes the stereo useless. However, either the mic or the ALC889
@@ -1780,12 +1782,33 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+
static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
+ spec->autocfg.speaker_pins[1] = 0x16;
+ spec->autocfg.speaker_pins[2] = 0x17;
}
static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
@@ -1808,6 +1831,16 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
spec->autocfg.speaker_pins[2] = 0x1b;
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void alc889_power_eapd(struct hda_codec *codec, int power)
+{
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0);
+ snd_hda_codec_write(codec, 0x15, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0);
+}
+#endif
+
/*
* ALC880 3-stack model
*
@@ -2401,6 +2434,8 @@ static const char *alc_slave_sws[] = {
"Speaker Playback Switch",
"Mono Playback Switch",
"IEC958 Playback Switch",
+ "Line-Out Playback Switch",
+ "PCM Playback Switch",
NULL,
};
@@ -2410,12 +2445,14 @@ static const char *alc_slave_sws[] = {
static void alc_free_kctls(struct hda_codec *codec);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
static struct snd_kcontrol_new alc_beep_mixer[] = {
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
{ } /* end */
};
+#endif
static int alc_build_controls(struct hda_codec *codec)
{
@@ -2452,6 +2489,7 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* create beep controls if needed */
if (spec->beep_amp) {
struct snd_kcontrol_new *knew;
@@ -2461,11 +2499,13 @@ static int alc_build_controls(struct hda_codec *codec)
if (!kctl)
return -ENOMEM;
kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, kctl);
+ err = snd_hda_ctl_add(codec,
+ get_amp_nid_(spec->beep_amp), kctl);
if (err < 0)
return err;
}
}
+#endif
/* if we have no master control, let's create it */
if (!spec->no_analog &&
@@ -2779,8 +2819,7 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
@@ -3480,7 +3519,7 @@ static int alc_build_pcms(struct hda_codec *codec)
snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
"%s Analog", codec->chip_name);
info->name = spec->stream_name_analog;
-
+
if (spec->stream_analog_playback) {
if (snd_BUG_ON(!spec->multiout.dac_nids))
return -EINVAL;
@@ -3595,12 +3634,29 @@ static void alc_free(struct hda_codec *codec)
snd_hda_detach_beep_device(codec);
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int alc_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ struct alc_spec *spec = codec->spec;
+ if (spec && spec->power_hook)
+ spec->power_hook(codec, 0);
+ return 0;
+}
+#endif
+
#ifdef SND_HDA_NEEDS_RESUME
static int alc_resume(struct hda_codec *codec)
{
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ struct alc_spec *spec = codec->spec;
+#endif
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (spec && spec->power_hook)
+ spec->power_hook(codec, 1);
+#endif
return 0;
}
#endif
@@ -3617,6 +3673,7 @@ static struct hda_codec_ops alc_patch_ops = {
.resume = alc_resume,
#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .suspend = alc_suspend,
.check_power_status = alc_check_power_status,
#endif
};
@@ -4322,10 +4379,26 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
+static int add_control_with_pfx(struct alc_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, unsigned long val)
+{
+ char name[32];
+ snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ return add_control(spec, type, name, val);
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+
#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
@@ -4379,7 +4452,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- char name[32];
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
@@ -4392,26 +4464,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
if (i == 2) {
/* Center/LFE */
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Center Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid, 1, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "LFE Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid, 2, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "Center Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid, 1, 2,
HDA_INPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "LFE Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid, 2, 2,
HDA_INPUT));
if (err < 0)
@@ -4423,14 +4495,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "Speaker";
else
pfx = chname[i];
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 2,
HDA_INPUT));
if (err < 0)
@@ -4446,7 +4516,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
{
hda_nid_t nid;
int err;
- char name[32];
if (!pin)
return 0;
@@ -4460,21 +4529,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
spec->multiout.extra_out_nid[0] = nid;
/* control HP volume/switch on the output mixer amp */
nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
if (err < 0)
return err;
} else if (alc880_is_multi_pin(pin)) {
/* set manual connection */
/* we have only a switch on HP-out PIN */
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -4487,16 +4553,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
const char *ctlname,
int idx, hda_nid_t mix_nid)
{
- char name[32];
int err;
- sprintf(name, "%s Playback Volume", ctlname);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", ctlname);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -4684,9 +4747,9 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
spec->multiout.dig_out_nid = dig_nid;
else {
spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- spec->slave_dig_outs[i - 1] = dig_nid;
- if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
break;
+ spec->slave_dig_outs[i - 1] = dig_nid;
}
}
if (spec->autocfg.dig_in_pin)
@@ -4773,8 +4836,12 @@ static void set_capture_mixer(struct hda_codec *codec)
}
}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
#define set_beep_amp(spec, nid, idx, dir) \
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
/*
* OK, here we have finally the patch for ALC880
@@ -5087,11 +5154,8 @@ static struct hda_verb alc260_hp_unsol_verbs[] = {
static void alc260_hp_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x10, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x10);
alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
}
@@ -5156,11 +5220,8 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
static void alc260_hp_3013_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
}
@@ -5173,12 +5234,8 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
static void alc260_hp_3012_automute(struct hda_codec *codec)
{
- unsigned int present, bits;
-
- present = snd_hda_codec_read(codec, 0x10, 0,
- AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+ unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
- bits = present ? 0 : PIN_OUT;
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
bits);
snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
@@ -5748,8 +5805,7 @@ static void alc260_replacer_672v_automute(struct hda_codec *codec)
unsigned int present;
/* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
- present = snd_hda_codec_read(codec, 0x0f, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x0f);
if (present) {
snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, 1);
@@ -5989,7 +6045,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
{
hda_nid_t nid_vol;
unsigned long vol_val, sw_val;
- char name[32];
int err;
if (nid >= 0x0f && nid < 0x11) {
@@ -6009,14 +6064,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
if (!(*vol_bits & (1 << nid_vol))) {
/* first control for the volume widget */
- snprintf(name, sizeof(name), "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
if (err < 0)
return err;
*vol_bits |= (1 << nid_vol);
}
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val);
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
if (err < 0)
return err;
return 1;
@@ -6246,10 +6299,11 @@ static const char *alc260_models[ALC260_MODEL_LAST] = {
static struct snd_pci_quirk alc260_cfg_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
+ SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL),
SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
+ SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */
SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
@@ -6619,7 +6673,7 @@ static struct hda_input_mux alc889A_mb31_capture_source = {
/* Front Mic (0x01) unused */
{ "Line", 0x2 },
/* Line 2 (0x03) unused */
- /* CD (0x04) unsused? */
+ /* CD (0x04) unused? */
},
};
@@ -7051,6 +7105,20 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc885_imac91_mixer[] = {
+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+ HDA_BIND_MUTE ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
+ { } /* end */
+};
+
+
static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -7336,8 +7404,8 @@ static struct snd_kcontrol_new alc882_macpro_mixer[] = {
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
/* FIXME: this looks suspicious...
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT),
*/
{ } /* end */
};
@@ -7506,6 +7574,66 @@ static struct hda_verb alc885_mbp3_init_verbs[] = {
{ }
};
+/* iMac 9,1 */
+static struct hda_verb alc885_imac91_init_verbs[] = {
+ /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Rear mixer */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* HP Pin: output 0 (0x0c) */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ /* Internal Speakers: output 0 (0x0d) */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Mic (rear) pin: input vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Front Mic pin: input vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line In pin: use output 1 when in LineOut mode */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* ADC1: mute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ADC2: mute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ADC3: mute amp left and right */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ { }
+};
+
/* iMac 24 mixer. */
static struct snd_kcontrol_new alc885_imac24_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
@@ -7552,6 +7680,26 @@ static void alc885_mbp3_setup(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
}
+static void alc885_imac91_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+
+}
+
+static void alc885_imac91_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ /* Headphone insertion or removal. */
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc885_imac91_automute(codec);
+}
static struct hda_verb alc882_targa_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -8184,12 +8332,8 @@ static void alc883_mitac_setup(struct hda_codec *codec)
/*
static void alc883_mitac_mic_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
*/
@@ -8411,10 +8555,8 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = {
/* toggle front-jack and RCA according to the hp-jack state */
static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x1b);
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -8424,10 +8566,8 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
/* toggle RCA according to the front-jack state */
static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x14);
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -8468,8 +8608,7 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
{
unsigned int present;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
@@ -8520,24 +8659,16 @@ static void alc883_haier_w66_setup(struct hda_codec *codec)
static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
}
static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
{
- unsigned int present;
- unsigned char bits;
+ int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -8688,8 +8819,7 @@ static void alc889A_mb31_automute(struct hda_codec *codec)
/* Mute only in 2ch or 4ch mode */
if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
== 0x00) {
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
@@ -8737,6 +8867,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
[ALC885_MB5] = "mb5",
[ALC885_MBP3] = "mbp3",
[ALC885_IMAC24] = "imac24",
+ [ALC885_IMAC91] = "imac91",
[ALC883_3ST_2ch_DIG] = "3stack-2ch-dig",
[ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
[ALC883_3ST_6ch] = "3stack-6ch",
@@ -8839,7 +8970,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO),
SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
@@ -8910,11 +9041,13 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
+ SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91),
SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
- /* FIXME: HP jack sense seems not working for MBP 5,1, so apparently
- * no perfect solution yet
+ /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
+ * so apparently no perfect solution yet
*/
SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
+ SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
{} /* terminator */
};
@@ -9020,6 +9153,20 @@ static struct alc_config_preset alc882_presets[] = {
.setup = alc885_imac24_setup,
.init_hook = alc885_imac24_init_hook,
},
+ [ALC885_IMAC91] = {
+ .mixers = { alc885_imac91_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc885_imac91_init_verbs,
+ alc880_gpio1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .channel_mode = alc885_mbp_4ch_modes,
+ .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
+ .input_mux = &alc882_capture_source,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .unsol_event = alc885_imac91_unsol_event,
+ .init_hook = alc885_imac91_automute,
+ },
[ALC882_TARGA] = {
.mixers = { alc882_targa_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
@@ -9186,6 +9333,7 @@ static struct alc_config_preset alc882_presets[] = {
.dac_nids = alc883_dac_nids,
.adc_nids = alc883_adc_nids_alt,
.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
+ .capsrc_nids = alc883_capsrc_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
@@ -9282,10 +9430,11 @@ static struct alc_config_preset alc882_presets[] = {
.init_hook = alc_automute_amp,
},
[ALC888_ACER_ASPIRE_8930G] = {
- .mixers = { alc888_base_mixer,
+ .mixers = { alc889_acer_aspire_8930g_mixer,
alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc889_acer_aspire_8930g_verbs },
+ alc889_acer_aspire_8930g_verbs,
+ alc889_eapd_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
@@ -9302,6 +9451,9 @@ static struct alc_config_preset alc882_presets[] = {
.unsol_event = alc_automute_amp_unsol_event,
.setup = alc889_acer_aspire_8930g_setup,
.init_hook = alc_automute_amp,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .power_hook = alc889_power_eapd,
+#endif
},
[ALC888_ACER_ASPIRE_7730G] = {
.mixers = { alc883_3ST_6ch_mixer,
@@ -9332,6 +9484,7 @@ static struct alc_config_preset alc882_presets[] = {
.dac_nids = alc883_dac_nids,
.adc_nids = alc883_adc_nids_alt,
.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
+ .capsrc_nids = alc883_capsrc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
@@ -9393,6 +9546,7 @@ static struct alc_config_preset alc882_presets[] = {
.dac_nids = alc883_dac_nids,
.adc_nids = alc883_adc_nids_alt,
.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
+ .capsrc_nids = alc883_capsrc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_lenovo_101e_capture_source,
@@ -9572,6 +9726,7 @@ static struct alc_config_preset alc882_presets[] = {
alc880_gpio1_init_verbs },
.adc_nids = alc883_adc_nids,
.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .capsrc_nids = alc883_capsrc_nids,
.dac_nids = alc883_dac_nids,
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.channel_mode = alc889A_mb31_6ch_modes,
@@ -9813,9 +9968,9 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
spec->multiout.dig_out_nid = dig_nid;
else {
spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- spec->slave_dig_outs[i - 1] = dig_nid;
- if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
break;
+ spec->slave_dig_outs[i - 1] = dig_nid;
}
}
if (spec->autocfg.dig_in_pin)
@@ -9926,10 +10081,12 @@ static int patch_alc882(struct hda_codec *codec)
spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
if (!spec->adc_nids && spec->input_mux) {
- int i;
+ int i, j;
spec->num_adc_nids = 0;
for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
+ const struct hda_input_mux *imux = spec->input_mux;
hda_nid_t cap;
+ hda_nid_t items[16];
hda_nid_t nid = alc882_adc_nids[i];
unsigned int wcap = get_wcaps(codec, nid);
/* get type */
@@ -9940,6 +10097,15 @@ static int patch_alc882(struct hda_codec *codec)
err = snd_hda_get_connections(codec, nid, &cap, 1);
if (err < 0)
continue;
+ err = snd_hda_get_connections(codec, cap, items,
+ ARRAY_SIZE(items));
+ if (err < 0)
+ continue;
+ for (j = 0; j < imux->num_items; j++)
+ if (imux->items[j].index >= err)
+ break;
+ if (j < imux->num_items)
+ continue;
spec->private_capsrc_nids[spec->num_adc_nids] = cap;
spec->num_adc_nids++;
}
@@ -10031,10 +10197,8 @@ static void alc262_hp_master_update(struct hda_codec *codec)
static void alc262_hp_bpc_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int presence;
- presence = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
alc262_hp_master_update(codec);
}
@@ -10048,10 +10212,8 @@ static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
static void alc262_hp_wildwest_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int presence;
- presence = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
alc262_hp_master_update(codec);
}
@@ -10285,13 +10447,8 @@ static void alc262_hippo_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, hp_nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
alc262_hippo_master_update(codec);
}
@@ -10580,6 +10737,13 @@ static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = {
{}
};
+static struct hda_verb alc262_lenovo_3000_init_verbs[] = {
+ /* Front Mic pin: input vref at 50% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {}
+};
+
static struct hda_input_mux alc262_fujitsu_capture_source = {
.num_items = 3,
.items = {
@@ -10617,21 +10781,8 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
- /* check laptop HP jack */
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
- /* check docking HP jack */
- present |= snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- if (present & AC_PINSENSE_PRESENCE)
- spec->jack_present = 1;
- else
- spec->jack_present = 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
+ snd_hda_jack_detect(codec, 0x1b);
spec->sense_updated = 1;
}
/* unmute internal speaker only if both HPs are unplugged and
@@ -10676,12 +10827,7 @@ static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present_int_hp;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
- present_int_hp = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present_int_hp & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
spec->sense_updated = 1;
}
if (spec->jack_present) {
@@ -10873,12 +11019,7 @@ static void alc262_ultra_automute(struct hda_codec *codec)
mute = 0;
/* auto-mute only when HP is used as HP */
if (!spec->cur_mux[0]) {
- unsigned int present;
- /* need to execute and sync at first */
- snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x15);
if (spec->jack_present)
mute = HDA_AMP_MUTE;
}
@@ -10955,7 +11096,6 @@ static int alc262_check_volbit(hda_nid_t nid)
static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
const char *pfx, int *vbits)
{
- char name[32];
unsigned long val;
int vbit;
@@ -10965,28 +11105,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
if (*vbits & vbit) /* a volume control for this mixer already there */
return 0;
*vbits |= vbit;
- snprintf(name, sizeof(name), "%s Playback Volume", pfx);
if (vbit == 2)
val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+ return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
}
static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
const char *pfx)
{
- char name[32];
unsigned long val;
if (!nid)
return 0;
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
if (nid == 0x16)
val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
}
/* add playback controls from the parsed DAC table */
@@ -11460,8 +11597,12 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
+ SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
+#if 0 /* disable the quirk since model=auto works better in recent versions */
SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
ALC262_SONY_ASSAMD),
+#endif
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
ALC262_TOSHIBA_RX1),
SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -11645,7 +11786,8 @@ static struct alc_config_preset alc262_presets[] = {
[ALC262_LENOVO_3000] = {
.mixers = { alc262_lenovo_3000_mixer },
.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_lenovo_3000_unsol_verbs },
+ alc262_lenovo_3000_unsol_verbs,
+ alc262_lenovo_3000_init_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
.hp_nid = 0x03,
@@ -11920,10 +12062,7 @@ static void alc268_acer_automute(struct hda_codec *codec, int force)
unsigned int mute;
if (force || !spec->sense_updated) {
- unsigned int present;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- spec->jack_present = (present & 0x80000000) != 0;
+ spec->jack_present = snd_hda_jack_detect(codec, 0x14);
spec->sense_updated = 1;
}
if (spec->jack_present)
@@ -12042,8 +12181,7 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -12324,11 +12462,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = {
static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
{
- char name[32];
hda_nid_t dac;
int err;
- sprintf(name, "%s Playback Volume", ctlname);
switch (nid) {
case 0x14:
case 0x16:
@@ -12342,7 +12478,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
}
if (spec->multiout.dac_nids[0] != dac &&
spec->multiout.dac_nids[1] != dac) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
HDA_COMPOSE_AMP_VAL(dac, 3, idx,
HDA_OUTPUT));
if (err < 0)
@@ -12350,12 +12486,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
}
- sprintf(name, "%s Playback Switch", ctlname);
if (nid != 0x16)
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
else /* mono */
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
if (err < 0)
return err;
@@ -12385,8 +12520,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->speaker_pins[0];
if (nid == 0x1d) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Speaker Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
if (err < 0)
return err;
@@ -12404,8 +12538,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Mono Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -12791,7 +12924,7 @@ static int patch_alc268(struct hda_codec *codec)
int board_config;
int i, has_beep, err;
- spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
@@ -13031,8 +13164,7 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -13057,12 +13189,10 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
unsigned char bits;
/* Check laptop headphone socket */
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x15);
/* Check port replicator headphone socket */
- present |= snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present |= snd_hda_jack_detect(codec, 0x1a);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -13086,11 +13216,8 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
unsigned int present_laptop;
unsigned int present_dock;
- present_laptop = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-
- present_dock = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present_laptop = snd_hda_jack_detect(codec, 0x18);
+ present_dock = snd_hda_jack_detect(codec, 0x1b);
/* Laptop mic port overrides dock mic port, design decision */
if (present_dock)
@@ -13172,11 +13299,12 @@ static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
/* toggle speaker-output according to the hp-jack state */
static void alc269_speaker_automute(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+ unsigned int nid = spec->autocfg.hp_pins[0];
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, nid);
bits = present ? AMP_IN_MUTE(0) : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -13401,8 +13529,8 @@ static void alc269_auto_init(struct hda_codec *codec)
static const char *alc269_models[ALC269_MODEL_LAST] = {
[ALC269_BASIC] = "basic",
[ALC269_QUANTA_FL1] = "quanta",
- [ALC269_ASUS_EEEPC_P703] = "eeepc-p703",
- [ALC269_ASUS_EEEPC_P901] = "eeepc-p901",
+ [ALC269_ASUS_AMIC] = "asus-amic",
+ [ALC269_ASUS_DMIC] = "asus-dmic",
[ALC269_FUJITSU] = "fujitsu",
[ALC269_LIFEBOOK] = "lifebook",
[ALC269_AUTO] = "auto",
@@ -13411,18 +13539,41 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
static struct snd_pci_quirk alc269_cfg_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
- ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_EEEPC_P703),
- SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_EEEPC_P703),
+ ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80JT", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_AMIC),
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
- ALC269_ASUS_EEEPC_P901),
+ ALC269_ASUS_DMIC),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
- ALC269_ASUS_EEEPC_P901),
- SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_EEEPC_P901),
+ ALC269_ASUS_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_ASUS_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_ASUS_DMIC),
SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
{}
@@ -13452,7 +13603,7 @@ static struct alc_config_preset alc269_presets[] = {
.setup = alc269_quanta_fl1_setup,
.init_hook = alc269_quanta_fl1_init_hook,
},
- [ALC269_ASUS_EEEPC_P703] = {
+ [ALC269_ASUS_AMIC] = {
.mixers = { alc269_eeepc_mixer },
.cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
@@ -13466,7 +13617,7 @@ static struct alc_config_preset alc269_presets[] = {
.setup = alc269_eeepc_amic_setup,
.init_hook = alc269_eeepc_inithook,
},
- [ALC269_ASUS_EEEPC_P901] = {
+ [ALC269_ASUS_DMIC] = {
.mixers = { alc269_eeepc_mixer },
.cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
@@ -13522,6 +13673,15 @@ static int patch_alc269(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC259", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ }
+
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models,
alc269_cfg_tbl);
@@ -14154,10 +14314,8 @@ static struct hda_verb alc861_toshiba_init_verbs[] = {
/* toggle speaker-output according to the hp-jack state */
static void alc861_toshiba_automute(struct hda_codec *codec)
{
- unsigned int present;
+ unsigned int present = snd_hda_jack_detect(codec, 0x0f);
- present = snd_hda_codec_read(codec, 0x0f, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
@@ -14257,9 +14415,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
}
@@ -14624,6 +14780,27 @@ static struct alc_config_preset alc861_presets[] = {
},
};
+/* Pin config fixes */
+enum {
+ PINFIX_FSC_AMILO_PI1505,
+};
+
+static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+};
+
+static const struct alc_fixup alc861_fixups[] = {
+ [PINFIX_FSC_AMILO_PI1505] = {
+ .pins = alc861_fsc_amilo_pi1505_pinfix
+ },
+};
+
+static struct snd_pci_quirk alc861_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+ {}
+};
static int patch_alc861(struct hda_codec *codec)
{
@@ -14647,6 +14824,8 @@ static int patch_alc861(struct hda_codec *codec)
board_config = ALC861_AUTO;
}
+ alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups);
+
if (board_config == ALC861_AUTO) {
/* automatic parse from the BIOS config */
err = alc861_parse_auto_config(codec);
@@ -15064,9 +15243,9 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x18);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
HDA_AMP_MUTE, bits);
}
@@ -15383,7 +15562,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- char name[32];
static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
hda_nid_t nid_v, nid_s;
int i, err;
@@ -15400,26 +15578,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
if (i == 2) {
/* Center/LFE */
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Center Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "LFE Playback Volume",
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "Center Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "Center",
HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
HDA_INPUT));
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "LFE Playback Switch",
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+ "LFE",
HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
HDA_INPUT));
if (err < 0)
@@ -15434,8 +15612,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "PCM";
} else
pfx = chname[i];
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
HDA_OUTPUT));
if (err < 0)
@@ -15443,8 +15620,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
if (cfg->line_outs == 1 &&
cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker";
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
HDA_INPUT));
if (err < 0)
@@ -15462,7 +15638,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
{
hda_nid_t nid_v, nid_s;
int err;
- char name[32];
if (!pin)
return 0;
@@ -15480,21 +15655,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
nid_s = alc861vd_idx_to_mixer_switch(
alc880_fixed_pin_idx(pin));
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
if (err < 0)
return err;
} else if (alc880_is_multi_pin(pin)) {
/* set manual connection */
/* we have only a switch on HP-out PIN */
- sprintf(name, "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -16080,6 +16252,52 @@ static struct snd_kcontrol_new alc663_g50v_mixer[] = {
{ } /* end */
};
+static struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc663_mode7_mixer[] = {
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
+ HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
+ HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc663_mode8_mixer[] = {
+ HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
+ HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
+ HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
+ HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+
static struct snd_kcontrol_new alc662_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -16367,6 +16585,45 @@ static struct hda_verb alc272_dell_init_verbs[] = {
{}
};
+static struct hda_verb alc663_mode7_init_verbs[] = {
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
+static struct hda_verb alc663_mode8_init_verbs[] = {
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
@@ -16384,9 +16641,9 @@ static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x14, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x14);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
}
@@ -16396,9 +16653,9 @@ static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ present = snd_hda_jack_detect(codec, 0x1b);
bits = present ? HDA_AMP_MUTE : 0;
+
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -16457,9 +16714,7 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16472,9 +16727,7 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16491,9 +16744,7 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
AMP_IN_MUTE(0), bits);
@@ -16510,9 +16761,7 @@ static void alc662_f5z_speaker_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x1b);
bits = present ? 0 : PIN_OUT;
snd_hda_codec_write(codec, 0x14, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
@@ -16522,12 +16771,8 @@ static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
{
unsigned int present1, present2;
- present1 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present1 = snd_hda_jack_detect(codec, 0x21);
+ present2 = snd_hda_jack_detect(codec, 0x15);
if (present1 || present2) {
snd_hda_codec_write_cache(codec, 0x14, 0,
@@ -16542,12 +16787,8 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
{
unsigned int present1, present2;
- present1 = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present1 = snd_hda_jack_detect(codec, 0x1b);
+ present2 = snd_hda_jack_detect(codec, 0x15);
if (present1 || present2) {
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -16562,6 +16803,54 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
}
}
+static void alc663_two_hp_m7_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present1, present2;
+
+ present1 = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ present2 = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+
+ if (present1 || present2) {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ } else {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ }
+}
+
+static void alc663_two_hp_m8_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present1, present2;
+
+ present1 = snd_hda_codec_read(codec, 0x21, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+ present2 = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+
+ if (present1 || present2) {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ } else {
+ snd_hda_codec_write_cache(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ }
+}
+
static void alc663_m51va_unsol_event(struct hda_codec *codec,
unsigned int res)
{
@@ -16581,7 +16870,7 @@ static void alc663_m51va_setup(struct hda_codec *codec)
spec->ext_mic.pin = 0x18;
spec->ext_mic.mux_idx = 0;
spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 1;
+ spec->int_mic.mux_idx = 9;
spec->auto_mic = 1;
}
@@ -16593,7 +16882,17 @@ static void alc663_m51va_inithook(struct hda_codec *codec)
/* ***************** Mode1 ******************************/
#define alc663_mode1_unsol_event alc663_m51va_unsol_event
-#define alc663_mode1_setup alc663_m51va_setup
+
+static void alc663_mode1_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
+}
+
#define alc663_mode1_inithook alc663_m51va_inithook
/* ***************** Mode2 ******************************/
@@ -16610,7 +16909,7 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec,
}
}
-#define alc662_mode2_setup alc663_m51va_setup
+#define alc662_mode2_setup alc663_mode1_setup
static void alc662_mode2_inithook(struct hda_codec *codec)
{
@@ -16631,7 +16930,7 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec,
}
}
-#define alc663_mode3_setup alc663_m51va_setup
+#define alc663_mode3_setup alc663_mode1_setup
static void alc663_mode3_inithook(struct hda_codec *codec)
{
@@ -16652,7 +16951,7 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec,
}
}
-#define alc663_mode4_setup alc663_m51va_setup
+#define alc663_mode4_setup alc663_mode1_setup
static void alc663_mode4_inithook(struct hda_codec *codec)
{
@@ -16673,7 +16972,7 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec,
}
}
-#define alc663_mode5_setup alc663_m51va_setup
+#define alc663_mode5_setup alc663_mode1_setup
static void alc663_mode5_inithook(struct hda_codec *codec)
{
@@ -16694,7 +16993,7 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec,
}
}
-#define alc663_mode6_setup alc663_m51va_setup
+#define alc663_mode6_setup alc663_mode1_setup
static void alc663_mode6_inithook(struct hda_codec *codec)
{
@@ -16702,14 +17001,56 @@ static void alc663_mode6_inithook(struct hda_codec *codec)
alc_mic_automute(codec);
}
+/* ***************** Mode7 ******************************/
+static void alc663_mode7_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_two_hp_m7_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc_mic_automute(codec);
+ break;
+ }
+}
+
+#define alc663_mode7_setup alc663_mode1_setup
+
+static void alc663_mode7_inithook(struct hda_codec *codec)
+{
+ alc663_two_hp_m7_speaker_automute(codec);
+ alc_mic_automute(codec);
+}
+
+/* ***************** Mode8 ******************************/
+static void alc663_mode8_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc663_two_hp_m8_speaker_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc_mic_automute(codec);
+ break;
+ }
+}
+
+#define alc663_mode8_setup alc663_m51va_setup
+
+static void alc663_mode8_inithook(struct hda_codec *codec)
+{
+ alc663_two_hp_m8_speaker_automute(codec);
+ alc_mic_automute(codec);
+}
+
static void alc663_g71v_hp_automute(struct hda_codec *codec)
{
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x21);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
@@ -16722,9 +17063,7 @@ static void alc663_g71v_front_automute(struct hda_codec *codec)
unsigned int present;
unsigned char bits;
- present = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
+ present = snd_hda_jack_detect(codec, 0x15);
bits = present ? HDA_AMP_MUTE : 0;
snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
HDA_AMP_MUTE, bits);
@@ -16840,6 +17179,8 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
[ALC663_ASUS_MODE4] = "asus-mode4",
[ALC663_ASUS_MODE5] = "asus-mode5",
[ALC663_ASUS_MODE6] = "asus-mode6",
+ [ALC663_ASUS_MODE7] = "asus-mode7",
+ [ALC663_ASUS_MODE8] = "asus-mode8",
[ALC272_DELL] = "dell",
[ALC272_DELL_ZM1] = "dell-zm1",
[ALC272_SAMSUNG_NC10] = "samsung-nc10",
@@ -16856,12 +17197,22 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3),
SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA),
SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2),
@@ -16911,6 +17262,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
ALC663_ASUS_H13),
+ SND_PCI_QUIRK(0x8086, 0xd604, "Intel mobo", ALC662_3ST_2ch_DIG),
{}
};
@@ -17144,6 +17496,36 @@ static struct alc_config_preset alc662_presets[] = {
.setup = alc663_mode6_setup,
.init_hook = alc663_mode6_inithook,
},
+ [ALC663_ASUS_MODE7] = {
+ .mixers = { alc663_mode7_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
+ .init_verbs = { alc662_init_verbs,
+ alc663_mode7_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .unsol_event = alc663_mode7_unsol_event,
+ .setup = alc663_mode7_setup,
+ .init_hook = alc663_mode7_inithook,
+ },
+ [ALC663_ASUS_MODE8] = {
+ .mixers = { alc663_mode8_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
+ .init_verbs = { alc662_init_verbs,
+ alc663_mode8_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .hp_nid = 0x03,
+ .dac_nids = alc662_dac_nids,
+ .dig_out_nid = ALC662_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+ .channel_mode = alc662_3ST_2ch_modes,
+ .unsol_event = alc663_mode8_unsol_event,
+ .setup = alc663_mode8_setup,
+ .init_hook = alc663_mode8_inithook,
+ },
[ALC272_DELL] = {
.mixers = { alc663_m51va_mixer },
.cap_mixer = alc272_auto_capture_mixer,
@@ -17261,21 +17643,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
return 0;
}
-static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- sprintf(name, "%s Playback Volume", pfx);
- return add_control(spec, ALC_CTL_WIDGET_VOL, name,
+ return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
}
-static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
- char name[32];
- sprintf(name, "%s Playback Switch", pfx);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
}
@@ -17353,13 +17731,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
return 0;
nid = alc662_look_for_dac(codec, pin);
if (!nid) {
- char name[32];
/* the corresponding DAC is already occupied */
if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
return 0; /* no way */
/* create a switch only */
- sprintf(name, "%s Playback Switch", pfx);
- return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
}
@@ -17535,6 +17911,15 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ if (alc_read_coef_idx(codec, 0)==0x8020){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC661", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ }
+
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models,
alc662_cfg_tbl);
@@ -17601,6 +17986,20 @@ static int patch_alc662(struct hda_codec *codec)
return 0;
}
+static int patch_alc888(struct hda_codec *codec)
+{
+ if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ return patch_alc662(codec);
+ }
+ return patch_alc882(codec);
+}
+
/*
* patch entries
*/
@@ -17610,7 +18009,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
{ .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
+ { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
{ .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
+ { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -17632,8 +18033,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
.patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
+ { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 66c0876bf73..eeda7beeb57 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pci.h>
+#include <linux/dmi.h>
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/jack.h>
@@ -92,6 +93,7 @@ enum {
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
+ STAC_92HD83XXX_HP,
STAC_92HD83XXX_MODELS
};
@@ -207,6 +209,7 @@ struct sigmatel_spec {
unsigned int gpio_data;
unsigned int gpio_mute;
unsigned int gpio_led;
+ unsigned int gpio_led_polarity;
/* stream */
unsigned int stream_delay;
@@ -1084,7 +1087,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (!spec->auto_mic && spec->num_dmuxes > 0 &&
snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&stac_dmux_mixer, codec));
if (err < 0)
return err;
@@ -1100,7 +1103,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
- err = snd_hda_ctl_add(codec,
+ err = snd_hda_ctl_add(codec, 0,
snd_ctl_new1(&stac_smux_mixer, codec));
if (err < 0)
return err;
@@ -1536,6 +1539,13 @@ static unsigned int alienware_m17x_pin_configs[13] = {
0x904601b0,
};
+static unsigned int intel_dg45id_pin_configs[14] = {
+ 0x02214230, 0x02A19240, 0x01013214, 0x01014210,
+ 0x01A19250, 0x01011212, 0x01016211, 0x40f000f0,
+ 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x014510A0,
+ 0x074510B0, 0x40f000f0
+};
+
static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
[STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
[STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
@@ -1543,6 +1553,7 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
[STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
[STAC_DELL_EQ] = dell_m6_pin_configs,
[STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
+ [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs,
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
@@ -1589,6 +1600,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
"Dell Studio 17", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
"Dell Studio 1555", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
+ "Dell Studio 1557", STAC_DELL_M6_DMIC),
{} /* terminator */
};
@@ -1621,6 +1634,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
[STAC_92HD83XXX_REF] = "ref",
[STAC_92HD83XXX_PWR_REF] = "mic-ref",
[STAC_DELL_S14] = "dell-s14",
+ [STAC_92HD83XXX_HP] = "hp",
};
static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1631,6 +1645,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD83XXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
"unknown Dell", STAC_DELL_S14),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
+ "HP", STAC_92HD83XXX_HP),
{} /* terminator */
};
@@ -1693,6 +1709,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
"HP dv4-1222nr", STAC_HP_DV4_1222NR),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
+ "HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
"HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@@ -2086,6 +2104,7 @@ static unsigned int ref9205_pin_configs[12] = {
10280204
1028021F
10280228 (Dell Vostro 1500)
+ 10280229 (Dell Vostro 1700)
*/
static unsigned int dell_9205_m42_pin_configs[12] = {
0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
@@ -2171,6 +2190,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
"Dell Inspiron", STAC_9205_DELL_M44),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
"Dell Vostro 1500", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229,
+ "Dell Vostro 1700", STAC_9205_DELL_M42),
/* Gateway */
SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
@@ -2643,6 +2664,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
+ STAC_CTL_WIDGET_MUTE_BEEP,
STAC_CTL_WIDGET_MONO_MUX,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
@@ -2653,6 +2675,7 @@ enum {
static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
STAC_MONO_MUX,
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
@@ -2664,7 +2687,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
static struct snd_kcontrol_new *
stac_control_new(struct sigmatel_spec *spec,
struct snd_kcontrol_new *ktemp,
- const char *name)
+ const char *name,
+ hda_nid_t nid)
{
struct snd_kcontrol_new *knew;
@@ -2680,6 +2704,8 @@ stac_control_new(struct sigmatel_spec *spec,
spec->kctls.alloced--;
return NULL;
}
+ if (nid)
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
return knew;
}
@@ -2688,7 +2714,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
int idx, const char *name,
unsigned long val)
{
- struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+ struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
+ get_amp_nid_(val));
if (!knew)
return -ENOMEM;
knew->index = idx;
@@ -2759,7 +2786,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
if (!spec->num_adcs || imux->num_items <= 1)
return 0; /* no need for input source control */
knew = stac_control_new(spec, &stac_input_src_temp,
- stac_input_src_temp.name);
+ stac_input_src_temp.name, 0);
if (!knew)
return -ENOMEM;
knew->count = spec->num_adcs;
@@ -3216,12 +3243,15 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
{
struct sigmatel_spec *spec = codec->spec;
u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- int err;
+ int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
+
+ if (spec->anabeep_nid == nid)
+ type = STAC_CTL_WIDGET_MUTE;
/* check for mute support for the the amp */
if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
- "PC Beep Playback Switch",
+ err = stac92xx_add_control(spec, type,
+ "Beep Playback Switch",
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -3230,7 +3260,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
/* check to see if there is volume support for the amp */
if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
- "PC Beep Playback Volume",
+ "Beep Playback Volume",
HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
if (err < 0)
return err;
@@ -3253,12 +3283,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int enabled = !!ucontrol->value.integer.value[0];
- if (codec->beep->enabled != enabled) {
- codec->beep->enabled = enabled;
- return 1;
- }
- return 0;
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
}
static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
@@ -3271,7 +3296,7 @@ static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
{
return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
- 0, "PC Beep Playback Switch", 0);
+ 0, "Beep Playback Switch", 0);
}
#endif
@@ -3626,6 +3651,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
}
}
+static int is_dual_headphones(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, valid_hps;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
+ spec->autocfg.hp_outs <= 1)
+ return 0;
+ valid_hps = 0;
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ hda_nid_t nid = spec->autocfg.hp_pins[i];
+ unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
+ continue;
+ valid_hps++;
+ }
+ return (valid_hps > 1);
+}
+
+
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
{
struct sigmatel_spec *spec = codec->spec;
@@ -3642,8 +3687,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
/* If we have no real line-out pin and multiple hp-outs, HPs should
* be set up as multi-channel outputs.
*/
- if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
- spec->autocfg.hp_outs > 1) {
+ if (is_dual_headphones(codec)) {
/* Copy hp_outs to line_outs, backup line_outs in
* speaker_outs so that the following routines can handle
* HP pins as primary outputs.
@@ -3738,15 +3782,16 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
err = snd_hda_attach_beep_device(codec, nid);
if (err < 0)
return err;
- /* IDT/STAC codecs have linear beep tone parameter */
- codec->beep->linear_tone = 1;
- /* if no beep switch is available, make its own one */
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- if (codec->beep &&
- !((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT)) {
- err = stac92xx_beep_switch_ctl(codec);
- if (err < 0)
- return err;
+ if (codec->beep) {
+ /* IDT/STAC codecs have linear beep tone parameter */
+ codec->beep->linear_tone = 1;
+ /* if no beep switch is available, make its own one */
+ caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ if (!(caps & AC_AMPCAP_MUTE)) {
+ err = stac92xx_beep_switch_ctl(codec);
+ if (err < 0)
+ return err;
+ }
}
}
#endif
@@ -4324,6 +4369,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
snd_array_free(&spec->kctls);
}
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid;
+
+ /* reset each pin before powering down DAC/ADC to avoid click noise */
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wcaps);
+ if (wid_type == AC_WID_PIN)
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
+}
+
static void stac92xx_free(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4331,6 +4398,7 @@ static void stac92xx_free(struct hda_codec *codec)
if (! spec)
return;
+ stac92xx_shutup(codec);
stac92xx_free_jacks(codec);
snd_array_free(&spec->events);
@@ -4381,12 +4449,16 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl & ~flag);
}
-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
{
if (!nid)
return 0;
- if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
- & (1 << 31))
+ /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT
+ * codecs behave wrongly when SET_PIN_SENSE is triggered, although
+ * the pincap gives TRIG_REQ bit.
+ */
+ if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE)
return 1;
return 0;
}
@@ -4665,6 +4737,82 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
+/*
+ * This method searches for the mute LED GPIO configuration
+ * provided as OEM string in SMBIOS. The format of that string
+ * is HP_Mute_LED_P_G or HP_Mute_LED_P
+ * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
+ * that corresponds to the NOT muted state of the master volume
+ * and G is the index of the GPIO to use as the mute LED control (0..9)
+ * If _G portion is missing it is assigned based on the codec ID
+ *
+ * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
+ * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
+ */
+static int find_mute_led_gpio(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
+
+ if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
+ NULL, dev))) {
+ if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
+ &spec->gpio_led_polarity,
+ &spec->gpio_led) == 2) {
+ spec->gpio_led = 1 << spec->gpio_led;
+ return 1;
+ }
+ if (sscanf(dev->name, "HP_Mute_LED_%d",
+ &spec->gpio_led_polarity) == 1) {
+ switch (codec->vendor_id) {
+ case 0x111d7608:
+ /* GPIO 0 */
+ spec->gpio_led = 0x01;
+ return 1;
+ case 0x111d7600:
+ case 0x111d7601:
+ case 0x111d7602:
+ case 0x111d7603:
+ /* GPIO 3 */
+ spec->gpio_led = 0x08;
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int hp_blike_system(u32 subsystem_id)
+{
+ switch (subsystem_id) {
+ case 0x103c1520:
+ case 0x103c1521:
+ case 0x103c1523:
+ case 0x103c1524:
+ case 0x103c1525:
+ case 0x103c1722:
+ case 0x103c1723:
+ case 0x103c1724:
+ case 0x103c1725:
+ case 0x103c1726:
+ case 0x103c1727:
+ case 0x103c1728:
+ case 0x103c1729:
+ case 0x103c172a:
+ case 0x103c172b:
+ case 0x103c307e:
+ case 0x103c307f:
+ case 0x103c3080:
+ case 0x103c3081:
+ case 0x103c7007:
+ case 0x103c7008:
+ return 1;
+ }
+ return 0;
+}
+
#ifdef CONFIG_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
@@ -4754,6 +4902,11 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
else
spec->gpio_data |= spec->gpio_led; /* white */
+ if (!spec->gpio_led_polarity) {
+ /* LED state is inverted on these systems */
+ spec->gpio_data ^= spec->gpio_led;
+ }
+
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir,
spec->gpio_data);
@@ -4761,28 +4914,28 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
return 0;
}
-#endif
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec,
+ hda_nid_t nid)
{
struct sigmatel_spec *spec = codec->spec;
- int i;
- hda_nid_t nid;
- /* reset each pin before powering down DAC/ADC to avoid click noise */
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (wid_type == AC_WID_PIN)
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- }
+ if (nid != 0x13)
+ return 0;
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE)
+ spec->gpio_data |= spec->gpio_led; /* mute LED on */
+ else
+ spec->gpio_data &= ~spec->gpio_led; /* mute LED off */
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
+ return 0;
+}
+
+#endif
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ stac92xx_shutup(codec);
return 0;
}
#endif
@@ -4797,6 +4950,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
.suspend = stac92xx_suspend,
.resume = stac92xx_resume,
#endif
+ .reboot_notify = stac92xx_shutup,
};
static int patch_stac9200(struct hda_codec *codec)
@@ -5142,6 +5296,22 @@ again:
break;
}
+ codec->patch_ops = stac92xx_patch_ops;
+
+ if (spec->board_config == STAC_92HD83XXX_HP)
+ spec->gpio_led = 0x01;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (spec->gpio_led) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ /* register check_power_status callback. */
+ codec->patch_ops.check_power_status =
+ idt92hd83xxx_hp_check_power_status;
+ }
+#endif
+
err = stac92xx_parse_auto_config(codec, 0x1d, 0);
if (!err) {
if (spec->board_config < 0) {
@@ -5177,8 +5347,6 @@ again:
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL, num_dacs);
- codec->patch_ops = stac92xx_patch_ops;
-
codec->proc_widget_hook = stac92hd_proc_hook;
return 0;
@@ -5243,6 +5411,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+ unsigned int pin_cfg;
int err = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -5426,6 +5595,29 @@ again:
break;
}
+ if (hp_blike_system(codec->subsystem_id)) {
+ pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
+ if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into slaved HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
+ | (AC_JACK_HP_OUT <<
+ AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
+ | AC_DEFCFG_SEQUENCE)))
+ | 0x1f;
+ snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
+ }
+ }
+
+ if (find_mute_led_gpio(codec))
+ snd_printd("mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (spec->gpio_led) {
spec->gpio_mask |= spec->gpio_led;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ee89db90c9b..b70e26ad263 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1,10 +1,10 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
+ * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
*
- * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
- * Takashi Iwai <tiwai@suse.de>
+ * (C) 2006-2009 VIA Technology, Inc.
+ * (C) 2006-2008 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
@@ -22,21 +22,27 @@
*/
/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
-/* */
+/* */
/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
-/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
-/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
+/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
+/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
-/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
-/* 2007-09-17 Lydia Wang Add VT1708B codec support */
+/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
+/* 2007-09-17 Lydia Wang Add VT1708B codec support */
/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
-/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
-/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
-/* 2008-04-09 Lydia Wang Add Independent HP feature */
+/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
+/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
+/* 2008-04-09 Lydia Wang Add Independent HP feature */
/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
-/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
-/* */
+/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
+/* 2009-02-16 Logan Li Add support for VT1718S */
+/* 2009-03-13 Logan Li Add support for VT1716S */
+/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
+/* 2009-07-08 Lydia Wang Add support for VT2002P */
+/* 2009-07-21 Lydia Wang Add support for VT1812 */
+/* 2009-09-19 Lydia Wang Add support for VT1818S */
+/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -76,14 +82,6 @@
#define VT1702_HP_NID 0x17
#define VT1702_DIGOUT_NID 0x11
-#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
-#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
-#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
-#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723)
-#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727)
-#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397)
-#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398)
-
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
@@ -92,12 +90,76 @@ enum VIA_HDA_CODEC {
VT1708B_8CH,
VT1708B_4CH,
VT1708S,
+ VT1708BCE,
VT1702,
+ VT1718S,
+ VT1716S,
+ VT2002P,
+ VT1812,
CODEC_TYPES,
};
-static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+struct via_spec {
+ /* codec parameterization */
+ struct snd_kcontrol_new *mixers[6];
+ unsigned int num_mixers;
+
+ struct hda_verb *init_verbs[5];
+ unsigned int num_iverbs;
+
+ char *stream_name_analog;
+ struct hda_pcm_stream *stream_analog_playback;
+ struct hda_pcm_stream *stream_analog_capture;
+
+ char *stream_name_digital;
+ struct hda_pcm_stream *stream_digital_playback;
+ struct hda_pcm_stream *stream_digital_capture;
+
+ /* playback */
+ struct hda_multi_out multiout;
+ hda_nid_t slave_dig_outs[2];
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t mux_nids[3];
+ hda_nid_t dig_in_nid;
+ hda_nid_t dig_in_pin;
+
+ /* capture source */
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[3];
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ struct hda_input_mux private_imux[2];
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
+ /* HP mode source */
+ const struct hda_input_mux *hp_mux;
+ unsigned int hp_independent_mode;
+ unsigned int hp_independent_mode_index;
+ unsigned int smart51_enabled;
+ unsigned int dmic_enabled;
+ enum VIA_HDA_CODEC codec_type;
+
+ /* work to check hp jack state */
+ struct hda_codec *codec;
+ struct delayed_work vt1708_hp_work;
+ int vt1708_jack_detectect;
+ int vt1708_hp_present;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ struct hda_loopback_check loopback;
+#endif
+};
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
{
+ u32 vendor_id = codec->vendor_id;
u16 ven_id = vendor_id >> 16;
u16 dev_id = vendor_id & 0xffff;
enum VIA_HDA_CODEC codec_type;
@@ -111,9 +173,11 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
codec_type = VT1709_10CH;
else if (dev_id >= 0xe714 && dev_id <= 0xe717)
codec_type = VT1709_6CH;
- else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+ else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
codec_type = VT1708B_8CH;
- else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+ if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
+ codec_type = VT1708BCE;
+ } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
codec_type = VT1708B_4CH;
else if ((dev_id & 0xfff) == 0x397
&& (dev_id >> 12) < 8)
@@ -121,6 +185,19 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
else if ((dev_id & 0xfff) == 0x398
&& (dev_id >> 12) < 8)
codec_type = VT1702;
+ else if ((dev_id & 0xfff) == 0x428
+ && (dev_id >> 12) < 8)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0433 || dev_id == 0xa721)
+ codec_type = VT1716S;
+ else if (dev_id == 0x0441 || dev_id == 0x4441)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0438 || dev_id == 0x4438)
+ codec_type = VT2002P;
+ else if (dev_id == 0x0448)
+ codec_type = VT1812;
+ else if (dev_id == 0x0440)
+ codec_type = VT1708S;
else
codec_type = UNKNOWN;
return codec_type;
@@ -128,10 +205,16 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
#define VIA_HP_EVENT 0x01
#define VIA_GPIO_EVENT 0x02
+#define VIA_JACK_EVENT 0x04
+#define VIA_MONO_EVENT 0x08
+#define VIA_SPEAKER_EVENT 0x10
+#define VIA_BIND_HP_EVENT 0x20
enum {
VIA_CTL_WIDGET_VOL,
VIA_CTL_WIDGET_MUTE,
+ VIA_CTL_WIDGET_ANALOG_MUTE,
+ VIA_CTL_WIDGET_BIND_PIN_MUTE,
};
enum {
@@ -141,99 +224,162 @@ enum {
AUTO_SEQ_SIDE
};
-/* Some VT1708S based boards gets the micboost setting wrong, so we have
- * to apply some brute-force and re-write the TLV's by software. */
-static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *_tlv)
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
+static void set_jack_power_state(struct hda_codec *codec);
+static int is_aa_path_mute(struct hda_codec *codec);
+
+static void vt1708_start_hp_work(struct via_spec *spec)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+ return;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+ !spec->vt1708_jack_detectect);
+ if (!delayed_work_pending(&spec->vt1708_hp_work))
+ schedule_delayed_work(&spec->vt1708_hp_work,
+ msecs_to_jiffies(100));
+}
- if (get_codec_type(codec->vendor_id) == VT1708S
- && (nid == 0x1a || nid == 0x1e)) {
- if (size < 4 * sizeof(unsigned int))
- return -ENOMEM;
- if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */
- return -EFAULT;
- if (put_user(2 * sizeof(unsigned int), _tlv + 1))
- return -EFAULT;
- if (put_user(0, _tlv + 2)) /* offset = 0 */
- return -EFAULT;
- if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
- return -EFAULT;
- }
- return 0;
+static void vt1708_stop_hp_work(struct via_spec *spec)
+{
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+ return;
+ if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
+ && !is_aa_path_mute(spec->codec))
+ return;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+ !spec->vt1708_jack_detectect);
+ cancel_delayed_work(&spec->vt1708_hp_work);
+ flush_scheduled_work();
}
-static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+
+static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- if (get_codec_type(codec->vendor_id) == VT1708S
- && (nid == 0x1a || nid == 0x1e)) {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 3;
+ set_jack_power_state(codec);
+ analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+ if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
+ if (is_aa_path_mute(codec))
+ vt1708_start_hp_work(codec->spec);
+ else
+ vt1708_stop_hp_work(codec->spec);
}
- return 0;
+ return change;
}
-static struct snd_kcontrol_new vt1708_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
-};
-
-
-struct via_spec {
- /* codec parameterization */
- struct snd_kcontrol_new *mixers[3];
- unsigned int num_mixers;
+/* modify .put = snd_hda_mixer_amp_switch_put */
+#define ANALOG_INPUT_MUTE \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = NULL, \
+ .index = 0, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = analog_input_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
- struct hda_verb *init_verbs[5];
- unsigned int num_iverbs;
+static void via_hp_bind_automute(struct hda_codec *codec);
- char *stream_name_analog;
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
-
- char *stream_name_digital;
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t slave_dig_outs[2];
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
- hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
+static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int i;
+ int change = 0;
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
+ long *valp = ucontrol->value.integer.value;
+ int lmute, rmute;
+ if (strstr(kcontrol->id.name, "Switch") == NULL) {
+ snd_printd("Invalid control!\n");
+ return change;
+ }
+ change = snd_hda_mixer_amp_switch_put(kcontrol,
+ ucontrol);
+ /* Get mute value */
+ lmute = *valp ? 0 : HDA_AMP_MUTE;
+ valp++;
+ rmute = *valp ? 0 : HDA_AMP_MUTE;
+
+ /* Set hp pins */
+ if (!spec->hp_independent_mode) {
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ snd_hda_codec_amp_update(
+ codec, spec->autocfg.hp_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ snd_hda_codec_amp_update(
+ codec, spec->autocfg.hp_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ }
+ }
- /* PCM information */
- struct hda_pcm pcm_rec[3];
+ if (!lmute && !rmute) {
+ /* Line Outs */
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.line_out_pins[i],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+ /* Speakers */
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_stereo(
+ codec, spec->autocfg.speaker_pins[i],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+ /* unmute */
+ via_hp_bind_automute(codec);
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux[2];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ } else {
+ if (lmute) {
+ /* Mute all left channels */
+ for (i = 1; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.line_out_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.speaker_pins[i],
+ 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ lmute);
+ }
+ if (rmute) {
+ /* mute all right channels */
+ for (i = 1; i < spec->autocfg.line_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.line_out_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ snd_hda_codec_amp_update(
+ codec,
+ spec->autocfg.speaker_pins[i],
+ 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ rmute);
+ }
+ }
+ return change;
+}
- /* HP mode source */
- const struct hda_input_mux *hp_mux;
- unsigned int hp_independent_mode;
+#define BIND_PIN_MUTE \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = NULL, \
+ .index = 0, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = bind_pin_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
+static struct snd_kcontrol_new via_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ ANALOG_INPUT_MUTE,
+ BIND_PIN_MUTE,
};
static hda_nid_t vt1708_adc_nids[2] = {
@@ -261,6 +407,27 @@ static hda_nid_t vt1702_adc_nids[3] = {
0x12, 0x20, 0x1F
};
+static hda_nid_t vt1718S_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+static hda_nid_t vt1716S_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x13, 0x14
+};
+
+static hda_nid_t vt2002P_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+static hda_nid_t vt1812_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x10, 0x11
+};
+
+
/* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val)
@@ -271,10 +438,12 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
knew = snd_array_new(&spec->kctls);
if (!knew)
return -ENOMEM;
- *knew = vt1708_control_templates[type];
+ *knew = via_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
knew->private_value = val;
return 0;
}
@@ -293,8 +462,8 @@ static void via_free_kctls(struct hda_codec *codec)
}
/* create input playback/capture controls for the given pin */
-static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
- const char *ctlname, int idx, int mix_nid)
+static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
+ int idx, int mix_nid)
{
char name[32];
int err;
@@ -305,7 +474,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -322,7 +491,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
}
@@ -343,10 +512,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
hda_nid_t pin;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ pin = spec->autocfg.hp_pins[i];
+ if (pin) /* connect to front */
+ via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ }
}
static void via_auto_init_analog_input(struct hda_codec *codec)
@@ -364,6 +536,502 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
}
}
+
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+
+static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int *affected_parm)
+{
+ unsigned parm;
+ unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
+ >> AC_DEFCFG_MISC_SHIFT
+ & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
+ unsigned present = snd_hda_jack_detect(codec, nid);
+ struct via_spec *spec = codec->spec;
+ if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+ || ((no_presence || present)
+ && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
+ *affected_parm = AC_PWRST_D0; /* if it's connected */
+ parm = AC_PWRST_D0;
+ } else
+ parm = AC_PWRST_D3;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+}
+
+static void set_jack_power_state(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int imux_is_smixer;
+ unsigned int parm;
+
+ if (spec->codec_type == VT1702) {
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ /* inputs */
+ /* PW 1/2/5 (14h/15h/18h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x14, &parm);
+ set_pin_power_state(codec, 0x15, &parm);
+ set_pin_power_state(codec, 0x18, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
+ /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW 3/4 (16h/17h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x16, &parm);
+ set_pin_power_state(codec, 0x17, &parm);
+ /* MW0 (1ah), AOW 0/1 (10h/1dh) */
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ } else if (spec->codec_type == VT1708B_8CH
+ || spec->codec_type == VT1708B_4CH
+ || spec->codec_type == VT1708S) {
+ /* SW0 (17h) = stereo mixer */
+ int is_8ch = spec->codec_type != VT1708B_4CH;
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
+ == ((spec->codec_type == VT1708S) ? 5 : 0);
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW 0/1 (13h/14h) */
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW6 (22h), SW2 (26h), AOW2 (24h) */
+ if (is_8ch) {
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x22, &parm);
+ snd_hda_codec_write(codec, 0x26, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x24, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* PW 3/4/7 (1ch/1dh/23h) */
+ parm = AC_PWRST_D3;
+ /* force to D0 for internal Speaker */
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ if (is_8ch)
+ set_pin_power_state(codec, 0x23, &parm);
+ /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ if (is_8ch) {
+ snd_hda_codec_write(codec, 0x25, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x27, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+ } else if (spec->codec_type == VT1718S) {
+ /* MUX6 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x27, &parm);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW2 (26h), AOW2 (ah) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW0/1 (24h/25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ set_pin_power_state(codec, 0x25, &parm);
+ if (!spec->hp_independent_mode) /* check for redirected HP */
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+ snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ if (spec->hp_independent_mode) {
+ /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x1b, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0xc, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+ } else if (spec->codec_type == VT1716S) {
+ unsigned int mono_out, present;
+ /* SW0 (17h) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 1/2/5 (1ah/1bh/1eh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1a, &parm);
+ set_pin_power_state(codec, 0x1b, &parm);
+ set_pin_power_state(codec, 0x1e, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* SW0 (17h), AIW0(13h) */
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1e, &parm);
+ /* PW11 (22h) */
+ if (spec->dmic_enabled)
+ set_pin_power_state(codec, 0x22, &parm);
+ else
+ snd_hda_codec_write(
+ codec, 0x22, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+ /* SW2(26h), AIW1(14h) */
+ snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* outputs */
+ /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x19, &parm);
+ /* Smart 5.1 PW2(1bh) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1b, &parm);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW7 (23h), SW3 (27h), AOW3 (25h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x23, &parm);
+ /* Smart 5.1 PW1(1ah) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1a, &parm);
+ snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* Smart 5.1 PW5(1eh) */
+ if (spec->smart51_enabled)
+ set_pin_power_state(codec, 0x1e, &parm);
+ snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* Mono out */
+ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
+ present = snd_hda_jack_detect(codec, 0x1c);
+ if (present)
+ mono_out = 0;
+ else {
+ present = snd_hda_jack_detect(codec, 0x1d);
+ if (!spec->hp_independent_mode && present)
+ mono_out = 0;
+ else
+ mono_out = 1;
+ }
+ parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
+ snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+ snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
+ parm);
+
+ /* PW 3/4 (1ch/1dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x1c, &parm);
+ set_pin_power_state(codec, 0x1d, &parm);
+ /* HP Independent Mode, power on AOW3 */
+ if (spec->hp_independent_mode)
+ snd_hda_codec_write(codec, 0x25, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* force to D0 for internal Speaker */
+ /* MW0 (16h), AOW0 (10h) */
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+ imux_is_smixer ? AC_PWRST_D0 : parm);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+ mono_out ? AC_PWRST_D0 : parm);
+ } else if (spec->codec_type == VT2002P) {
+ unsigned int present;
+ /* MUX9 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x1f, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x10, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ snd_hda_codec_write(codec, 0x8, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ /* PW4 (26h), MW4 (1ch), MUX4(37h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x26, &parm);
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x37,
+ 0, AC_VERB_SET_POWER_STATE, parm);
+
+ /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ snd_hda_codec_write(codec, 0x19, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x35, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ if (spec->hp_independent_mode) {
+ snd_hda_codec_write(codec, 0x9, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* Class-D */
+ /* PW0 (24h), MW0(18h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (present) {
+ snd_hda_codec_write(
+ codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(
+ codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(
+ codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ snd_hda_codec_write(
+ codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+
+ /* Mono Out */
+ /* PW15 (31h), MW8(17h), MUX8(3bh) */
+ present = snd_hda_jack_detect(codec, 0x26);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x31, &parm);
+ if (present) {
+ snd_hda_codec_write(
+ codec, 0x17, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(
+ codec, 0x3b, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(
+ codec, 0x17, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ snd_hda_codec_write(
+ codec, 0x3b, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+
+ /* MW9 (21h) */
+ if (imux_is_smixer || !is_aa_path_mute(codec))
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ else
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ } else if (spec->codec_type == VT1812) {
+ unsigned int present;
+ /* MUX10 (1eh) = stereo mixer */
+ imux_is_smixer = snd_hda_codec_read(
+ codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+ /* inputs */
+ /* PW 5/6/7 (29h/2ah/2bh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x29, &parm);
+ set_pin_power_state(codec, 0x2a, &parm);
+ set_pin_power_state(codec, 0x2b, &parm);
+ if (imux_is_smixer)
+ parm = AC_PWRST_D0;
+ /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+ snd_hda_codec_write(codec, 0x1e, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x1f, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x10, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* outputs */
+ /* AOW0 (8h)*/
+ snd_hda_codec_write(codec, 0x8, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ /* PW4 (28h), MW4 (18h), MUX4(38h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x28, &parm);
+ snd_hda_codec_write(codec, 0x18, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x38, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x25, &parm);
+ snd_hda_codec_write(codec, 0x15, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x35, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ if (spec->hp_independent_mode) {
+ snd_hda_codec_write(codec, 0x9, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ }
+
+ /* Internal Speaker */
+ /* PW0 (24h), MW0(14h), MUX0(34h) */
+ present = snd_hda_jack_detect(codec, 0x25);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x24, &parm);
+ if (present) {
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x34, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ }
+ /* Mono Out */
+ /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
+ present = snd_hda_jack_detect(codec, 0x28);
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x31, &parm);
+ if (present) {
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x3c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ snd_hda_codec_write(codec, 0x3e, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ } else {
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x3c, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ snd_hda_codec_write(codec, 0x3e, 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ }
+
+ /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
+ parm = AC_PWRST_D3;
+ set_pin_power_state(codec, 0x33, &parm);
+ snd_hda_codec_write(codec, 0x1d, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+ snd_hda_codec_write(codec, 0x3d, 0,
+ AC_VERB_SET_POWER_STATE, parm);
+
+ /* MW9 (21h) */
+ if (imux_is_smixer || !is_aa_path_mute(codec))
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ else
+ snd_hda_codec_write(
+ codec, 0x21, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ }
+}
+
/*
* input MUX handling
*/
@@ -395,6 +1063,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
if (!spec->mux_nids[adc_idx])
return -EINVAL;
+ /* switch to D0 beofre change index */
+ if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
+ snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ /* update jack power state */
+ set_jack_power_state(codec);
+
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
spec->mux_nids[adc_idx],
&spec->cur_mux[adc_idx]);
@@ -413,16 +1089,74 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.hp_pins[0];
- unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
-
+ hda_nid_t nid;
+ unsigned int pinsel;
+
+ switch (spec->codec_type) {
+ case VT1718S:
+ nid = 0x34;
+ break;
+ case VT2002P:
+ nid = 0x35;
+ break;
+ case VT1812:
+ nid = 0x3d;
+ break;
+ default:
+ nid = spec->autocfg.hp_pins[0];
+ break;
+ }
+ /* use !! to translate conn sel 2 for VT1718S */
+ pinsel = !!snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL,
+ 0x00);
ucontrol->value.enumerated.item[0] = pinsel;
return 0;
}
+static void activate_ctl(struct hda_codec *codec, const char *name, int active)
+{
+ struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
+ if (ctl) {
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ctl->vd[0].access |= active
+ ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
+ }
+}
+
+static int update_side_mute_status(struct hda_codec *codec)
+{
+ /* mute side channel */
+ struct via_spec *spec = codec->spec;
+ unsigned int parm = spec->hp_independent_mode
+ ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
+ hda_nid_t sw3;
+
+ switch (spec->codec_type) {
+ case VT1708:
+ sw3 = 0x1b;
+ break;
+ case VT1709_10CH:
+ sw3 = 0x29;
+ break;
+ case VT1708B_8CH:
+ case VT1708S:
+ sw3 = 0x27;
+ break;
+ default:
+ sw3 = 0;
+ break;
+ }
+
+ if (sw3)
+ snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ parm);
+ return 0;
+}
+
static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -430,47 +1164,46 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
struct via_spec *spec = codec->spec;
hda_nid_t nid = spec->autocfg.hp_pins[0];
unsigned int pinsel = ucontrol->value.enumerated.item[0];
- unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-
- if (con_nid == spec->multiout.hp_nid) {
- if (pinsel == 0) {
- if (!spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs -= 1;
- spec->hp_independent_mode = 1;
- }
- } else if (pinsel == 1) {
- if (spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs += 1;
- spec->hp_independent_mode = 0;
- }
- }
- } else {
- if (pinsel == 0) {
- if (spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs += 1;
- spec->hp_independent_mode = 0;
- }
- } else if (pinsel == 1) {
- if (!spec->hp_independent_mode) {
- if (spec->multiout.num_dacs > 1)
- spec->multiout.num_dacs -= 1;
- spec->hp_independent_mode = 1;
- }
- }
+ /* Get Independent Mode index of headphone pin widget */
+ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
+ ? 1 : 0;
+
+ switch (spec->codec_type) {
+ case VT1718S:
+ nid = 0x34;
+ pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
+ spec->multiout.num_dacs = 4;
+ break;
+ case VT2002P:
+ nid = 0x35;
+ break;
+ case VT1812:
+ nid = 0x3d;
+ break;
+ default:
+ nid = spec->autocfg.hp_pins[0];
+ break;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
+
+ if (spec->multiout.hp_nid && spec->multiout.hp_nid
+ != spec->multiout.dac_nids[HDA_FRONT])
+ snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
+ 0, 0, 0);
+
+ update_side_mute_status(codec);
+ /* update HP volume/swtich active state */
+ if (spec->codec_type == VT1708S
+ || spec->codec_type == VT1702
+ || spec->codec_type == VT1718S
+ || spec->codec_type == VT1716S
+ || spec->codec_type == VT2002P
+ || spec->codec_type == VT1812) {
+ activate_ctl(codec, "Headphone Playback Volume",
+ spec->hp_independent_mode);
+ activate_ctl(codec, "Headphone Playback Switch",
+ spec->hp_independent_mode);
}
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
- pinsel);
-
- if (spec->multiout.hp_nid &&
- spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec,
- spec->multiout.hp_nid,
- 0, 0, 0);
-
return 0;
}
@@ -486,6 +1219,175 @@ static struct snd_kcontrol_new via_hp_mixer[] = {
{ } /* end */
};
+static void notify_aa_path_ctls(struct hda_codec *codec)
+{
+ int i;
+ struct snd_ctl_elem_id id;
+ const char *labels[] = {"Mic", "Front M