summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2010-09-14 11:26:55 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2010-09-14 11:26:55 +1000
commita959153ec5b343202e089bf2783f60612b4918de (patch)
tree7172674149b911b3b38be05e9154a0620bca628f /sound
parent9e654c734782c978d0a9b365d8b515f46567e1cb (diff)
parent3cec84bca62b14bca680185ace09c035ca0466f1 (diff)
Merge remote branch 'sound/for-next'
Diffstat (limited to 'sound')
-rw-r--r--sound/core/oss/mixer_oss.c22
-rw-r--r--sound/core/pcm.c3
-rw-r--r--sound/core/pcm_lib.c14
-rw-r--r--sound/core/pcm_native.c2
-rw-r--r--sound/drivers/Kconfig19
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/aloop.c1055
-rw-r--r--sound/drivers/virmidi.c2
-rw-r--r--sound/i2c/other/ak4xxx-adda.c2
-rw-r--r--sound/isa/Kconfig36
-rw-r--r--sound/isa/Makefile4
-rw-r--r--sound/isa/ad1816a/ad1816a.c2
-rw-r--r--sound/isa/azt2320.c2
-rw-r--r--sound/isa/galaxy/Makefile10
-rw-r--r--sound/isa/galaxy/azt1605.c91
-rw-r--r--sound/isa/galaxy/azt2316.c111
-rw-r--r--sound/isa/galaxy/galaxy.c652
-rw-r--r--sound/isa/gus/gusmax.c4
-rw-r--r--sound/isa/sb/sb8.c2
-rw-r--r--sound/isa/sgalaxy.c369
-rw-r--r--sound/oss/au1550_ac97.c18
-rw-r--r--sound/pci/Kconfig17
-rw-r--r--sound/pci/ca0106/ca0106_main.c34
-rw-r--r--sound/pci/emu10k1/emumpu401.c2
-rw-r--r--sound/pci/hda/hda_codec.c262
-rw-r--r--sound/pci/hda/hda_generic.c41
-rw-r--r--sound/pci/hda/hda_intel.c3
-rw-r--r--sound/pci/hda/hda_local.h26
-rw-r--r--sound/pci/hda/patch_analog.c41
-rw-r--r--sound/pci/hda/patch_ca0110.c8
-rw-r--r--sound/pci/hda/patch_cirrus.c42
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_realtek.c510
-rw-r--r--sound/pci/hda/patch_sigmatel.c315
-rw-r--r--sound/pci/hda/patch_via.c587
-rw-r--r--sound/pci/ice1712/delta.c10
-rw-r--r--sound/pci/ice1712/delta.h4
-rw-r--r--sound/pci/ice1712/pontis.c6
-rw-r--r--sound/pci/ice1712/prodigy192.c2
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c111
-rw-r--r--sound/pci/rme96.c8
-rw-r--r--sound/pci/rme9652/hdsp.c8
-rw-r--r--sound/ppc/snd_ps3.c2
-rw-r--r--sound/soc/s3c24xx/s3c-dma.c3
-rw-r--r--sound/usb/Kconfig2
-rw-r--r--sound/usb/caiaq/audio.c175
-rw-r--r--sound/usb/caiaq/control.c208
-rw-r--r--sound/usb/caiaq/device.c10
-rw-r--r--sound/usb/caiaq/device.h6
-rw-r--r--sound/usb/caiaq/input.c248
-rw-r--r--sound/usb/card.c31
-rw-r--r--sound/usb/endpoint.c2
-rw-r--r--sound/usb/helper.c17
-rw-r--r--sound/usb/midi.c9
-rw-r--r--sound/usb/pcm.c4
-rw-r--r--sound/usb/proc.c2
-rw-r--r--sound/usb/quirks-table.h161
-rw-r--r--sound/usb/urb.c2
58 files changed, 3833 insertions, 1509 deletions
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index f50ebf20df96..86afb13cd240 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -77,7 +77,7 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file)
struct snd_mixer_oss_file *fmixer;
if (file->private_data) {
- fmixer = (struct snd_mixer_oss_file *) file->private_data;
+ fmixer = file->private_data;
module_put(fmixer->card->module);
snd_card_file_remove(fmixer->card, file);
kfree(fmixer);
@@ -368,7 +368,7 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int
static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg);
+ return snd_mixer_oss_ioctl1(file->private_data, cmd, arg);
}
int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)
@@ -582,7 +582,7 @@ static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *left, int *right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
*left = *right = 100;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
@@ -691,7 +691,7 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int left, int right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
@@ -740,7 +740,7 @@ static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -753,7 +753,7 @@ static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -766,7 +766,7 @@ static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
return 0;
@@ -776,7 +776,7 @@ static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
return 0;
@@ -813,7 +813,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -861,7 +861,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -925,7 +925,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl
static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
{
- struct slot *p = (struct slot *)chn->private_data;
+ struct slot *p = chn->private_data;
if (p) {
if (p->allocated && p->assigned) {
kfree(p->assigned->name);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 204af48c5cc1..88525a958291 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -364,8 +364,7 @@ static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data,
- buffer);
+ snd_pcm_proc_info_read(entry->private_data, buffer);
}
static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index e23e0e7ab26f..a1707cca9c66 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -334,11 +334,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* delta = "expected next hw_ptr" for in_interrupt != 0 */
delta = runtime->hw_ptr_interrupt + runtime->period_size;
if (delta > new_hw_ptr) {
- hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
- hw_base = 0;
- new_hw_ptr = hw_base + pos;
- goto __delta;
+ /* check for double acknowledged interrupts */
+ hdelta = jiffies - runtime->hw_ptr_jiffies;
+ if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary)
+ hw_base = 0;
+ new_hw_ptr = hw_base + pos;
+ goto __delta;
+ }
}
}
/* new_hw_ptr might be lower than old_hw_ptr in case when */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 134fc6c2e08d..e2e73895db12 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -864,6 +864,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->hw_ptr_jiffies = jiffies;
+ runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
+ runtime->rate;
runtime->status->state = state;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 480c38623da8..c8961165277c 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -74,6 +74,25 @@ config SND_DUMMY
To compile this driver as a module, choose M here: the module
will be called snd-dummy.
+config SND_ALOOP
+ tristate "Generic loopback driver (PCM)"
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for the PCM loopback device.
+ This module returns played samples back to the user space using
+ the standard ALSA PCM device. The devices are routed 0->1 and
+ 1->0, where first number is the playback PCM device and second
+ number is the capture device. Module creates two PCM devices and
+ configured number of substreams (see the pcm_substreams module
+ parameter).
+
+ The looback device allow time sychronization with an external
+ timing source using the time shift universal control (+-20%
+ of system time).
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-aloop.
+
config SND_VIRMIDI
tristate "Virtual MIDI soundcard"
depends on SND_SEQUENCER
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index d4a07f9ff2c7..1a8440c8b138 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -4,6 +4,7 @@
#
snd-dummy-objs := dummy.o
+snd-aloop-objs := aloop.o
snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
@@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
new file mode 100644
index 000000000000..3123a15d23f6
--- /dev/null
+++ b/sound/drivers/aloop.c
@@ -0,0 +1,1055 @@
+/*
+ * Loopback soundcard
+ *
+ * Original code:
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * More accurate positioning and full-duplex support:
+ * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de>
+ *
+ * Major (almost complete) rewrite:
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ * A next major update in 2010 (separate timers for playback and capture):
+ * Copyright (c) Jaroslav Kysela <perex@perex.cz>
+ *
+ * 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/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("A loopback soundcard");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
+
+#define MAX_PCM_SUBSTREAMS 8
+
+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] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+static int pcm_notify[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for loopback soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this loopback soundcard.");
+module_param_array(pcm_substreams, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
+module_param_array(pcm_notify, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+
+#define NO_PITCH 100000
+
+struct loopback_pcm;
+
+struct loopback_cable {
+ spinlock_t lock;
+ struct loopback_pcm *streams[2];
+ struct snd_pcm_hardware hw;
+ /* flags */
+ unsigned int valid;
+ unsigned int running;
+};
+
+struct loopback_setup {
+ unsigned int notify: 1;
+ unsigned int rate_shift;
+ unsigned int format;
+ unsigned int rate;
+ unsigned int channels;
+ struct snd_ctl_elem_id active_id;
+ struct snd_ctl_elem_id format_id;
+ struct snd_ctl_elem_id rate_id;
+ struct snd_ctl_elem_id channels_id;
+};
+
+struct loopback {
+ struct snd_card *card;
+ struct mutex cable_lock;
+ struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
+ struct snd_pcm *pcm[2];
+ struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+};
+
+struct loopback_pcm {
+ struct loopback *loopback;
+ struct snd_pcm_substream *substream;
+ struct loopback_cable *cable;
+ unsigned int pcm_buffer_size;
+ unsigned int buf_pos; /* position in buffer */
+ unsigned int silent_size;
+ /* PCM parameters */
+ unsigned int pcm_period_size;
+ unsigned int pcm_bps; /* bytes per second */
+ unsigned int pcm_salign; /* bytes per sample * channels */
+ unsigned int pcm_rate_shift; /* rate shift value */
+ /* flags */
+ unsigned int period_update_pending :1;
+ /* timer stuff */
+ unsigned int irq_pos; /* fractional IRQ position */
+ unsigned int period_size_frac;
+ unsigned long last_jiffies;
+ struct timer_list timer;
+};
+
+static struct platform_device *devices[SNDRV_CARDS];
+
+static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+ if (dpcm->pcm_rate_shift == NO_PITCH) {
+ x /= HZ;
+ } else {
+ x = div_u64(NO_PITCH * (unsigned long long)x,
+ HZ * (unsigned long long)dpcm->pcm_rate_shift);
+ }
+ return x - (x % dpcm->pcm_salign);
+}
+
+static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+ if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */
+ return x * HZ;
+ } else {
+ x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ,
+ NO_PITCH);
+ }
+ return x;
+}
+
+static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm)
+{
+ int device = dpcm->substream->pstr->pcm->device;
+
+ if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ device ^= 1;
+ return &dpcm->loopback->setup[dpcm->substream->number][device];
+}
+
+static inline unsigned int get_notify(struct loopback_pcm *dpcm)
+{
+ return get_setup(dpcm)->notify;
+}
+
+static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
+{
+ return get_setup(dpcm)->rate_shift;
+}
+
+static void loopback_timer_start(struct loopback_pcm *dpcm)
+{
+ unsigned long tick;
+ unsigned int rate_shift = get_rate_shift(dpcm);
+
+ if (rate_shift != dpcm->pcm_rate_shift) {
+ dpcm->pcm_rate_shift = rate_shift;
+ dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
+ }
+ tick = dpcm->period_size_frac - dpcm->irq_pos;
+ tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
+ dpcm->timer.expires = jiffies + tick;
+ add_timer(&dpcm->timer);
+}
+
+static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+{
+ del_timer(&dpcm->timer);
+}
+
+#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+
+static int loopback_check_format(struct loopback_cable *cable, int stream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct loopback_setup *setup;
+ struct snd_card *card;
+ int check;
+
+ if (cable->valid != CABLE_VALID_BOTH) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ goto __notify;
+ return 0;
+ }
+ runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+ substream->runtime;
+ check = cable->hw.formats != (1ULL << runtime->format) ||
+ cable->hw.rate_min != runtime->rate ||
+ cable->hw.rate_max != runtime->rate ||
+ cable->hw.channels_min != runtime->channels ||
+ cable->hw.channels_max != runtime->channels;
+ if (!check)
+ return 0;
+ if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ return -EIO;
+ } else {
+ snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
+ substream, SNDRV_PCM_STATE_DRAINING);
+ __notify:
+ runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+ substream->runtime;
+ setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+ card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
+ if (setup->format != runtime->format) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->format_id);
+ setup->format = runtime->format;
+ }
+ if (setup->rate != runtime->rate) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->rate_id);
+ setup->rate = runtime->rate;
+ }
+ if (setup->channels != runtime->channels) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->channels_id);
+ setup->channels = runtime->channels;
+ }
+ }
+ return 0;
+}
+
+static void loopback_active_notify(struct loopback_pcm *dpcm)
+{
+ snd_ctl_notify(dpcm->loopback->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &get_setup(dpcm)->active_id);
+}
+
+static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = loopback_check_format(cable, substream->stream);
+ if (err < 0)
+ return err;
+ dpcm->last_jiffies = jiffies;
+ dpcm->pcm_rate_shift = 0;
+ loopback_timer_start(dpcm);
+ cable->running |= (1 << substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ cable->running &= ~(1 << substream->stream);
+ loopback_timer_stop(dpcm);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int loopback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ unsigned int bps, salign;
+
+ salign = (snd_pcm_format_width(runtime->format) *
+ runtime->channels) / 8;
+ bps = salign * runtime->rate;
+ if (bps <= 0 || salign <= 0)
+ return -EINVAL;
+
+ dpcm->buf_pos = 0;
+ dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* clear capture buffer */
+ dpcm->silent_size = dpcm->pcm_buffer_size;
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+ runtime->buffer_size * runtime->channels);
+ }
+
+ dpcm->irq_pos = 0;
+ dpcm->period_update_pending = 0;
+ dpcm->pcm_bps = bps;
+ dpcm->pcm_salign = salign;
+ dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ if (!(cable->valid & ~(1 << substream->stream))) {
+ cable->hw.formats = (1ULL << runtime->format);
+ cable->hw.rate_min = runtime->rate;
+ cable->hw.rate_max = runtime->rate;
+ cable->hw.channels_min = runtime->channels;
+ cable->hw.channels_max = runtime->channels;
+ }
+ cable->valid |= 1 << substream->stream;
+ mutex_unlock(&dpcm->loopback->cable_lock);
+
+ return 0;
+}
+
+static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
+ char *dst = runtime->dma_area;
+ unsigned int dst_off = dpcm->buf_pos;
+
+ if (dpcm->silent_size >= dpcm->pcm_buffer_size)
+ return;
+ if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size)
+ bytes = dpcm->pcm_buffer_size - dpcm->silent_size;
+
+ for (;;) {
+ unsigned int size = bytes;
+ if (dst_off + size > dpcm->pcm_buffer_size)
+ size = dpcm->pcm_buffer_size - dst_off;
+ snd_pcm_format_set_silence(runtime->format, dst + dst_off,
+ bytes_to_frames(runtime, size) *
+ runtime->channels);
+ dpcm->silent_size += size;
+ bytes -= size;
+ if (!bytes)
+ break;
+ dst_off = 0;
+ }
+}
+
+static void copy_play_buf(struct loopback_pcm *play,
+ struct loopback_pcm *capt,
+ unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = play->substream->runtime;
+ char *src = play->substream->runtime->dma_area;
+ char *dst = capt->substream->runtime->dma_area;
+ unsigned int src_off = play->buf_pos;
+ unsigned int dst_off = capt->buf_pos;
+ unsigned int clear_bytes = 0;
+
+ /* check if playback is draining, trim the capture copy size
+ * when our pointer is at the end of playback ring buffer */
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+ snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {
+ snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
+ appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
+ appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
+ appl_ptr1 += play->buf_pos / play->pcm_salign;
+ if (appl_ptr < appl_ptr1)
+ appl_ptr1 -= runtime->buffer_size;
+ diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
+ if (diff < bytes) {
+ clear_bytes = bytes - diff;
+ bytes = diff;
+ }
+ }
+
+ for (;;) {
+ unsigned int size = bytes;
+ if (src_off + size > play->pcm_buffer_size)
+ size = play->pcm_buffer_size - src_off;
+ if (dst_off + size > capt->pcm_buffer_size)
+ size = capt->pcm_buffer_size - dst_off;
+ memcpy(dst + dst_off, src + src_off, size);
+ capt->silent_size = 0;
+ bytes -= size;
+ if (!bytes)
+ break;
+ src_off = (src_off + size) % play->pcm_buffer_size;
+ dst_off = (dst_off + size) % capt->pcm_buffer_size;
+ }
+
+ if (clear_bytes > 0)
+ clear_capture_buf(capt, clear_bytes);
+}
+
+#define BYTEPOS_UPDATE_POSONLY 0
+#define BYTEPOS_UPDATE_CLEAR 1
+#define BYTEPOS_UPDATE_COPY 2
+
+static void loopback_bytepos_update(struct loopback_pcm *dpcm,
+ unsigned int delta,
+ unsigned int cmd)
+{
+ unsigned int count;
+ unsigned long last_pos;
+
+ last_pos = byte_pos(dpcm, dpcm->irq_pos);
+ dpcm->irq_pos += delta * dpcm->pcm_bps;
+ count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+ if (!count)
+ return;
+ if (cmd == BYTEPOS_UPDATE_CLEAR)
+ clear_capture_buf(dpcm, count);
+ else if (cmd == BYTEPOS_UPDATE_COPY)
+ copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
+ dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
+ count);
+ dpcm->buf_pos += count;
+ dpcm->buf_pos %= dpcm->pcm_buffer_size;
+ if (dpcm->irq_pos >= dpcm->period_size_frac) {
+ dpcm->irq_pos %= dpcm->period_size_frac;
+ dpcm->period_update_pending = 1;
+ }
+}
+
+static void loopback_pos_update(struct loopback_cable *cable)
+{
+ struct loopback_pcm *dpcm_play =
+ cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ struct loopback_pcm *dpcm_capt =
+ cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+ unsigned long delta_play = 0, delta_capt = 0;
+
+ spin_lock(&cable->lock);
+ if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+ delta_play = jiffies - dpcm_play->last_jiffies;
+ dpcm_play->last_jiffies += delta_play;
+ }
+
+ if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+ delta_capt = jiffies - dpcm_capt->last_jiffies;
+ dpcm_capt->last_jiffies += delta_capt;
+ }
+
+ if (delta_play == 0 && delta_capt == 0) {
+ spin_unlock(&cable->lock);
+ return;
+ }
+
+ if (delta_play > delta_capt) {
+ loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
+ BYTEPOS_UPDATE_POSONLY);
+ delta_play = delta_capt;
+ } else if (delta_play < delta_capt) {
+ loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
+ BYTEPOS_UPDATE_CLEAR);
+ delta_capt = delta_play;
+ }
+
+ if (delta_play == 0 && delta_capt == 0) {
+ spin_unlock(&cable->lock);
+ return;
+ }
+ /* note delta_capt == delta_play at this moment */
+ loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
+ loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+ spin_unlock(&cable->lock);
+}
+
+static void loopback_timer_function(unsigned long data)
+{
+ struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
+ int stream;
+
+ loopback_pos_update(dpcm->cable);
+ stream = dpcm->substream->stream;
+ if (dpcm->cable->running & (1 << stream))
+ loopback_timer_start(dpcm);
+ if (dpcm->period_update_pending) {
+ dpcm->period_update_pending = 0;
+ if (dpcm->cable->running & (1 << stream))
+ snd_pcm_period_elapsed(dpcm->substream);
+ }
+}
+
+static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+
+ loopback_pos_update(dpcm->cable);
+ return bytes_to_frames(runtime, dpcm->buf_pos);
+}
+
+static struct snd_pcm_hardware loopback_pcm_hardware =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 32,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 2 * 1024 * 1024,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ struct loopback_pcm *dpcm = runtime->private_data;
+ kfree(dpcm);
+}
+
+static int loopback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int loopback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ cable->valid &= ~(1 << substream->stream);
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static unsigned int get_cable_index(struct snd_pcm_substream *substream)
+{
+ if (!substream->pcm->device)
+ return substream->stream;
+ else
+ return !substream->stream;
+}
+
+static int loopback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback *loopback = substream->private_data;
+ struct loopback_pcm *dpcm;
+ struct loopback_cable *cable;
+ int err = 0;
+ int dev = get_cable_index(substream);
+
+ mutex_lock(&loopback->cable_lock);
+ dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+ if (!dpcm) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+ dpcm->loopback = loopback;
+ dpcm->substream = substream;
+ setup_timer(&dpcm->timer, loopback_timer_function,
+ (unsigned long)dpcm);
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable) {
+ cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+ if (!cable) {
+ kfree(dpcm);
+ err = -ENOMEM;
+ goto unlock;
+ }
+ spin_lock_init(&cable->lock);
+ cable->hw = loopback_pcm_hardware;
+ loopback->cables[substream->number][dev] = cable;
+ }
+ dpcm->cable = cable;
+ cable->streams[substream->stream] = dpcm;
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ runtime->private_data = dpcm;
+ runtime->private_free = loopback_runtime_free;
+ if (get_notify(dpcm) &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = loopback_pcm_hardware;
+ } else {
+ runtime->hw = cable->hw;
+ }
+ unlock:
+ mutex_unlock(&loopback->cable_lock);
+ return err;
+}
+
+static int loopback_close(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ struct loopback_pcm *dpcm = substream->runtime->private_data;
+ struct loopback_cable *cable;
+ int dev = get_cable_index(substream);
+
+ loopback_timer_stop(dpcm);
+ mutex_lock(&loopback->cable_lock);
+ cable = loopback->cables[substream->number][dev];
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ cable->streams[substream->stream] = NULL;
+ } else {
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+ mutex_unlock(&loopback->cable_lock);
+ return 0;
+}
+
+static struct snd_pcm_ops loopback_playback_ops = {
+ .open = loopback_open,
+ .close = loopback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = loopback_hw_params,
+ .hw_free = loopback_hw_free,
+ .prepare = loopback_prepare,
+ .trigger = loopback_trigger,
+ .pointer = loopback_pointer,
+};
+
+static struct snd_pcm_ops loopback_capture_ops = {
+ .open = loopback_open,
+ .close = loopback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = loopback_hw_params,
+ .hw_free = loopback_hw_free,
+ .prepare = loopback_prepare,
+ .trigger = loopback_trigger,
+ .pointer = loopback_pointer,
+};
+
+static int __devinit loopback_pcm_new(struct loopback *loopback,
+ int device, int substreams)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(loopback->card, "Loopback PCM", device,
+ substreams, substreams, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+
+ pcm->private_data = loopback;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "Loopback PCM");
+
+ loopback->pcm[device] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, 2 * 1024 * 1024);
+ return 0;
+}
+
+static int loopback_rate_shift_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 = 80000;
+ uinfo->value.integer.max = 120000;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift;
+ return 0;
+}
+
+static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0];
+ if (val < 80000)
+ val = 80000;
+ if (val > 120000)
+ val = 120000;
+ mutex_lock(&loopback->cable_lock);
+ if (val != loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift) {
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift = val;
+ change = 1;
+ }
+ mutex_unlock(&loopback->cable_lock);
+ return change;
+}
+
+static int loopback_notify_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify;
+ return 0;
+}
+
+static int loopback_notify_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0] ? 1 : 0;
+ if (val != loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify) {
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify = val;
+ change = 1;
+ }
+ return change;
+}
+
+static int loopback_active_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ struct loopback_cable *cable = loopback->cables
+ [kcontrol->id.subdevice][kcontrol->id.device];
+ unsigned int val = 0;
+
+ if (cable != NULL)
+ val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ 1 : 0;
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int loopback_format_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 = SNDRV_PCM_FORMAT_LAST;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].format;
+ return 0;
+}
+
+static int loopback_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;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate;
+ return 0;
+}
+
+static int loopback_channels_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 = 1;
+ uinfo->value.integer.max = 1024;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_channels_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate;
+ return 0;
+}
+
+static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Rate Shift 100000",
+ .info = loopback_rate_shift_info,
+ .get = loopback_rate_shift_get,
+ .put = loopback_rate_shift_put,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Notify",
+ .info = snd_ctl_boolean_mono_info,
+ .get = loopback_notify_get,
+ .put = loopback_notify_put,
+},
+#define ACTIVE_IDX 2
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Active",
+ .info = snd_ctl_boolean_mono_info,
+ .get = loopback_active_get,
+},
+#define FORMAT_IDX 3
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Format",
+ .info = loopback_format_info,
+ .get = loopback_format_get
+},
+#define RATE_IDX 4
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Rate",
+ .info = loopback_rate_info,
+ .get = loopback_rate_get
+},
+#define CHANNELS_IDX 5
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Channels",
+ .info = loopback_channels_info,
+ .get = loopback_channels_get
+}
+};
+
+static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+{
+ struct snd_card *card = loopback->card;
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
+ struct loopback_setup *setup;
+ int err, dev, substr, substr_count, idx;
+
+ strcpy(card->mixername, "Loopback Mixer");
+ for (dev = 0; dev < 2; dev++) {
+ pcm = loopback->pcm[dev];
+ substr_count =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+ for (substr = 0; substr < substr_count; substr++) {
+ setup = &loopback->setup[substr][dev];
+ setup->notify = notify;
+ setup->rate_shift = NO_PITCH;
+ setup->format = SNDRV_PCM_FORMAT_S16_LE;
+ setup->rate = 48000;
+ setup->channels = 2;
+ for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
+ idx++) {
+ kctl = snd_ctl_new1(&loopback_controls[idx],
+ loopback);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.device = dev;
+ kctl->id.subdevice = substr;
+ switch (idx) {
+ case ACTIVE_IDX:
+ setup->active_id = kctl->id;
+ break;
+ case FORMAT_IDX:
+ setup->format_id = kctl->id;
+ break;
+ case RATE_IDX:
+ setup->rate_id = kctl->id;
+ break;
+ case CHANNELS_IDX:
+ setup->channels_id = kctl->id;
+ break;
+ default:
+ break;
+ }
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+static int __devinit loopback_probe(struct platform_device *devptr)
+{
+ struct snd_card *card;
+ struct loopback *loopback;
+ int dev = devptr->id;
+ int err;
+
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct loopback), &card);
+ if (err < 0)
+ return err;
+ loopback = card->private_data;
+
+ if (pcm_substreams[dev] < 1)
+ pcm_substreams[dev] = 1;
+ if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+ pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+
+ loopback->card = card;
+ mutex_init(&loopback->cable_lock);
+
+ err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
+ if (err < 0)
+ goto __nodev;
+ err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
+ if (err < 0)
+ goto __nodev;
+ err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
+ if (err < 0)
+ goto __nodev;
+ strcpy(card->driver, "Loopback");
+ strcpy(card->shortname, "Loopback");
+ sprintf(card->longname, "Loopback %i", dev + 1);
+ err = snd_card_register(card);
+ if (!err) {
+ platform_set_drvdata(devptr, card);
+ return 0;
+ }
+ __nodev:
+ snd_card_free(card);
+ return err;
+}
+
+static int __devexit loopback_remove(struct platform_device *devptr)
+{
+ snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(devptr, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int loopback_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct loopback *loopback = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_pcm_suspend_all(loopback->pcm[0]);
+ snd_pcm_suspend_all(loopback->pcm[1]);
+ return 0;
+}
+
+static int loopback_resume(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
+#define SND_LOOPBACK_DRIVER "snd_aloop"
+
+static struct platform_driver loopback_driver = {
+ .probe = loopback_probe,
+ .remove = __devexit_p(loopback_remove),
+#ifdef CONFIG_PM
+ .suspend = loopback_suspend,
+ .resume = loopback_resume,
+#endif
+ .driver = {
+ .name = SND_LOOPBACK_DRIVER
+ },
+};
+
+static void loopback_unregister_all(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(devices); ++i)
+ platform_device_unregister(devices[i]);
+ platform_driver_unregister(&loopback_driver);
+}
+
+static int __init alsa_card_loopback_init(void)
+{
+ int i, err, cards;
+
+ err = platform_driver_register(&loopback_driver);
+ if (err < 0)
+ return err;
+
+
+ cards = 0;
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ struct platform_device *device;
+ if (!enable[i])
+ continue;
+ device = platform_device_register_simple(SND_LOOPBACK_DRIVER,
+ i, NULL, 0);
+ if (IS_ERR(device))
+ continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
+ devices[i] = device;
+ cards++;
+ }
+ if (!cards) {
+#ifdef MODULE
+ printk(KERN_ERR "aloop: No loopback enabled\n");
+#endif
+ loopback_unregister_all();
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit alsa_card_loopback_exit(void)
+{
+ loopback_unregister_all();
+}
+
+module_init(alsa_card_loopback_init)
+module_exit(alsa_card_loopback_exit)
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 0e631c3221e3..f4cd49336f33 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -94,7 +94,7 @@ static int __devinit snd_virmidi_probe(struct platform_device *devptr)
sizeof(struct snd_card_virmidi), &card);
if (err < 0)
return err;
- vmidi = (struct snd_card_virmidi *)card->private_data;
+ vmidi = card->private_data;
vmidi->card = card;
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 1adb8a3c2b62..ebab6c7aaa81 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -878,7 +878,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
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;
+ struct snd_akm4xxx *ak = entry->private_data;
int reg, val, chip;
for (chip = 0; chip < ak->num_chips; chip++) {
for (reg = 0; reg < ak->total_regs; reg++) {
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index c6990c680796..52064cfa91f3 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -77,6 +77,32 @@ config SND_ALS100
To compile this driver as a module, choose M here: the module
will be called snd-als100.
+config SND_AZT1605
+ tristate "Aztech AZT1605 Driver"
+ depends on SND
+ select SND_WSS_LIB
+ select SND_MPU401_UART
+ select SND_OPL3_LIB
+ help
+ Say Y here to include support for Aztech Sound Galaxy cards
+ based on the AZT1605 chipset.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-azt1605.
+
+config SND_AZT2316
+ tristate "Aztech AZT2316 Driver"
+ depends on SND
+ select SND_WSS_LIB
+ select SND_MPU401_UART
+ select SND_OPL3_LIB
+ help
+ Say Y here to include support for Aztech Sound Galaxy cards
+ based on the AZT2316 chipset.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-azt2316.
+
config SND_AZT2320
tristate "Aztech Systems AZT2320"
depends on PNP
@@ -351,16 +377,6 @@ config SND_SB16_CSP
coprocessor can do variable tasks like various compression and
decompression algorithms.
-config SND_SGALAXY
- tristate "Aztech Sound Galaxy"
- select SND_WSS_LIB
- help
- Say Y here to include support for Aztech Sound Galaxy
- soundcards.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-sgalaxy.
-
config SND_SSCAPE
tristate "Ensoniq SoundScape driver"
select SND_MPU401_UART
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index c73d30c4f462..8d781e419e2e 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o
snd-es18xx-objs := es18xx.o
snd-opl3sa2-objs := opl3sa2.o
snd-sc6000-objs := sc6000.o
-snd-sgalaxy-objs := sgalaxy.o
snd-sscape-objs := sscape.o
# Toplevel Module Dependency
@@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
-obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
-obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
+obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \
sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index bbcbf92a8ebe..3cb75bc97699 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -162,7 +162,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
sizeof(struct snd_card_ad1816a), &card);
if (error < 0)
return error;
- acard = (struct snd_card_ad1816a *)card->private_data;
+ acard = card->private_data;
if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index f7aa637b0d18..aac8dc15c2fe 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -188,7 +188,7 @@ static int __devinit snd_card_azt2320_probe(int dev,
sizeof(struct snd_card_azt2320), &card);
if (error < 0)
return error;
- acard = (struct snd_card_azt2320 *)card->private_data;
+ acard = card->private_data;
if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
new file mode 100644
index 000000000000..e307066d4315
--- /dev/null
+++ b/sound/isa/galaxy/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-azt1605-objs := azt1605.o
+snd-azt2316-objs := azt2316.o
+
+obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
+obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
new file mode 100644
index 000000000000..9a97643cb713
--- /dev/null
+++ b/sound/isa/galaxy/azt1605.c
@@ -0,0 +1,91 @@
+/*
+ * Aztech AZT1605 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT1605
+
+#define CRD_NAME "Aztech AZT1605"
+#define DRV_NAME "AZT1605"
+#define DEV_NAME "azt1605"
+
+#define GALAXY_DSP_MAJOR 2
+#define GALAXY_DSP_MINOR 1
+
+#define GALAXY_CONFIG_SIZE 3
+
+/*
+ * 24-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220 (0 << 0)
+#define GALAXY_CONFIG_SBA_240 (1 << 0)
+#define GALAXY_CONFIG_SBA_260 (2 << 0)
+#define GALAXY_CONFIG_SBA_280 (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_MPUA_300 (0 << 2)
+#define GALAXY_CONFIG_MPUA_330 (1 << 2)
+
+#define GALAXY_CONFIG_MPU_ENABLE (1 << 3)
+
+#define GALAXY_CONFIG_GAME_ENABLE (1 << 4)
+
+#define GALAXY_CONFIG_CD_PANASONIC (1 << 5)
+#define GALAXY_CONFIG_CD_MITSUMI (1 << 6)
+#define GALAXY_CONFIG_CD_MASK (\
+ GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI)
+
+#define GALAXY_CONFIG_UNUSED (1 << 7)
+#define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED
+
+#define GALAXY_CONFIG_SBIRQ_2 (1 << 8)
+#define GALAXY_CONFIG_SBIRQ_3 (1 << 9)
+#define GALAXY_CONFIG_SBIRQ_5 (1 << 10)
+#define GALAXY_CONFIG_SBIRQ_7 (1 << 11)
+
+#define GALAXY_CONFIG_MPUIRQ_2 (1 << 12)
+#define GALAXY_CONFIG_MPUIRQ_3 (1 << 13)
+#define GALAXY_CONFIG_MPUIRQ_5 (1 << 14)
+#define GALAXY_CONFIG_MPUIRQ_7 (1 << 15)
+
+#define GALAXY_CONFIG_WSSA_530 (0 << 16)
+#define GALAXY_CONFIG_WSSA_604 (1 << 16)
+#define GALAXY_CONFIG_WSSA_E80 (2 << 16)
+#define GALAXY_CONFIG_WSSA_F40 (3 << 16)
+
+#define GALAXY_CONFIG_WSS_ENABLE (1 << 18)
+
+#define GALAXY_CONFIG_CDIRQ_11 (1 << 19)
+#define GALAXY_CONFIG_CDIRQ_12 (1 << 20)
+#define GALAXY_CONFIG_CDIRQ_15 (1 << 21)
+#define GALAXY_CONFIG_CDIRQ_MASK (\
+ GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\
+ GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22)
+#define GALAXY_CONFIG_CDDMA_0 (1 << 22)
+#define GALAXY_CONFIG_CDDMA_1 (2 << 22)
+#define GALAXY_CONFIG_CDDMA_3 (3 << 22)
+#define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3
+
+#define GALAXY_CONFIG_MASK (\
+ GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\
+ GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\
+ GALAXY_CONFIG_CDDMA_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
new file mode 100644
index 000000000000..189441141df6
--- /dev/null
+++ b/sound/isa/galaxy/azt2316.c
@@ -0,0 +1,111 @@
+/*
+ * Aztech AZT2316 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT2316
+
+#define CRD_NAME "Aztech AZT2316"
+#define DRV_NAME "AZT2316"
+#define DEV_NAME "azt2316"
+
+#define GALAXY_DSP_MAJOR 3
+#define GALAXY_DSP_MINOR 1
+
+#define GALAXY_CONFIG_SIZE 4
+
+/*
+ * 32-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220 (0 << 0)
+#define GALAXY_CONFIG_SBA_240 (1 << 0)
+#define GALAXY_CONFIG_SBA_260 (2 << 0)
+#define GALAXY_CONFIG_SBA_280 (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_SBIRQ_2 (1 << 2)
+#define GALAXY_CONFIG_SBIRQ_5 (1 << 3)
+#define GALAXY_CONFIG_SBIRQ_7 (1 << 4)
+#define GALAXY_CONFIG_SBIRQ_10 (1 << 5)
+
+#define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6)
+#define GALAXY_CONFIG_SBDMA_0 (1 << 6)
+#define GALAXY_CONFIG_SBDMA_1 (2 << 6)
+#define GALAXY_CONFIG_SBDMA_3 (3 << 6)
+
+#define GALAXY_CONFIG_WSSA_530 (0 << 8)
+#define GALAXY_CONFIG_WSSA_604 (1 << 8)
+#define GALAXY_CONFIG_WSSA_E80 (2 << 8)
+#define GALAXY_CONFIG_WSSA_F40 (3 << 8)
+
+#define GALAXY_CONFIG_WSS_ENABLE (1 << 10)
+
+#define GALAXY_CONFIG_GAME_ENABLE (1 << 11)
+
+#define GALAXY_CONFIG_MPUA_300 (0 << 12)
+#define GALAXY_CONFIG_MPUA_330 (1 << 12)
+
+#define GALAXY_CONFIG_MPU_ENABLE (1 << 13)
+
+#define GALAXY_CONFIG_CDA_310 (0 << 14)
+#define GALAXY_CONFIG_CDA_320 (1 << 14)
+#define GALAXY_CONFIG_CDA_340 (2 << 14)
+#define GALAXY_CONFIG_CDA_350 (3 << 14)
+#define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350
+
+#define GALAXY_CONFIG_CD_DISABLE (0 << 16)
+#define GALAXY_CONFIG_CD_PANASONIC (1 << 16)
+#define GALAXY_CONFIG_CD_SONY (2 << 16)
+#define GALAXY_CONFIG_CD_MITSUMI (3 << 16)
+#define GALAXY_CONFIG_CD_AZTECH (4 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16)
+#define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7
+
+#define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20)
+#define GALAXY_CONFIG_CDDMA8_0 (1 << 20)
+#define GALAXY_CONFIG_CDDMA8_1 (2 << 20)
+#define GALAXY_CONFIG_CDDMA8_3 (3 << 20)
+#define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3
+
+#define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22)
+#define GALAXY_CONFIG_CDDMA16_5 (1 << 22)
+#define GALAXY_CONFIG_CDDMA16_6 (2 << 22)
+#define GALAXY_CONFIG_CDDMA16_7 (3 << 22)
+#define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7
+
+#define GALAXY_CONFIG_MPUIRQ_2 (1 << 24)
+#define GALAXY_CONFIG_MPUIRQ_5 (1 << 25)
+#define GALAXY_CONFIG_MPUIRQ_7 (1 << 26)
+#define GALAXY_CONFIG_MPUIRQ_10 (1 << 27)
+
+#define GALAXY_CONFIG_CDIRQ_5 (1 << 28)
+#define GALAXY_CONFIG_CDIRQ_11 (1 << 29)
+#define GALAXY_CONFIG_CDIRQ_12 (1 << 30)
+#define GALAXY_CONFIG_CDIRQ_15 (1 << 31)
+#define GALAXY_CONFIG_CDIRQ_MASK (\
+ GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\
+ GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_MASK (\
+ GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\
+ GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\
+ GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
new file mode 100644
index 000000000000..ee54df082b9c
--- /dev/null
+++ b/sound/isa/galaxy/galaxy.c
@@ -0,0 +1,652 @@
+/*
+ * Aztech AZT1605/AZT2316 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/wss.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+MODULE_DESCRIPTION(CRD_NAME);
+MODULE_AUTHOR("Rene Herman");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
+
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long fm_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 dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
+module_param_array(wss_port, long, NULL, 0444);
+MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
+module_param_array(mpu_irq, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
+
+/*
+ * Generic SB DSP support routines
+ */
+
+#define DSP_PORT_RESET 0x6
+#define DSP_PORT_READ 0xa
+#define DSP_PORT_COMMAND 0xc
+#define DSP_PORT_STATUS 0xc
+#define DSP_PORT_DATA_AVAIL 0xe
+
+#define DSP_SIGNATURE 0xaa
+
+#define DSP_COMMAND_GET_VERSION 0xe1
+
+static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+{
+ int loops = 1000;
+
+ while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
+ if (!loops--)
+ return -EIO;
+ cpu_relax();
+ }
+ *val = ioread8(port + DSP_PORT_READ);
+ return 0;
+}
+
+static int __devinit dsp_reset(void __iomem *port)
+{
+ u8 val;
+
+ iowrite8(1, port + DSP_PORT_RESET);
+ udelay(10);
+ iowrite8(0, port + DSP_PORT_RESET);
+
+ if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int __devinit dsp_command(void __iomem *port, u8 cmd)
+{
+ int loops = 1000;
+
+ while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
+ if (!loops--)
+ return -EIO;
+ cpu_relax();
+ }
+ iowrite8(cmd, port + DSP_PORT_COMMAND);
+ return 0;
+}
+
+static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+{
+ int err;
+
+ err = dsp_command(port, DSP_COMMAND_GET_VERSION);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(port, major);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(port, minor);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * Generic WSS support routines
+ */
+
+#define WSS_CONFIG_DMA_0 (1 << 0)
+#define WSS_CONFIG_DMA_1 (2 << 0)
+#define WSS_CONFIG_DMA_3 (3 << 0)
+#define WSS_CONFIG_DUPLEX (1 << 2)
+#define WSS_CONFIG_IRQ_7 (1 << 3)
+#define WSS_CONFIG_IRQ_9 (2 << 3)
+#define WSS_CONFIG_IRQ_10 (3 << 3)
+#define WSS_CONFIG_IRQ_11 (4 << 3)
+
+#define WSS_PORT_CONFIG 0
+#define WSS_PORT_SIGNATURE 3
+
+#define WSS_SIGNATURE 4
+
+static int __devinit wss_detect(void __iomem *wss_port)
+{
+ if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void wss_set_config(void __iomem *wss_port, u8 wss_config)
+{
+ iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
+}
+
+/*
+ * Aztech Sound Galaxy specifics
+ */
+
+#define GALAXY_PORT_CONFIG 1024
+#define CONFIG_PORT_SET 4
+
+#define DSP_COMMAND_GALAXY_8 8
+#define GALAXY_COMMAND_GET_TYPE 5
+
+#define DSP_COMMAND_GALAXY_9 9
+#define GALAXY_COMMAND_WSSMODE 0
+#define GALAXY_COMMAND_SB8MODE 1
+
+#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE
+#define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE
+
+struct snd_galaxy {
+ void __iomem *port;
+ void __iomem *config_port;
+ void __iomem *wss_port;
+ u32 config;
+ struct resource *res_port;
+ struct resource *res_config_port;
+ struct resource *res_wss_port;
+};
+
+static u32 config[SNDRV_CARDS];
+static u8 wss_config[SNDRV_CARDS];
+
+static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+{
+ if (!enable[n])
+ return 0;
+
+ switch (port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_err(dev, "please specify port\n");
+ return 0;
+ case 0x220:
+ config[n] |= GALAXY_CONFIG_SBA_220;
+ break;
+ case 0x240:
+ config[n] |= GALAXY_CONFIG_SBA_240;
+ break;
+ case 0x260:
+ config[n] |= GALAXY_CONFIG_SBA_260;
+ break;
+ case 0x280:
+ config[n] |= GALAXY_CONFIG_SBA_280;
+ break;
+ default:
+ dev_err(dev, "invalid port %#lx\n", port[n]);
+ return 0;
+ }
+
+ switch (wss_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_err(dev, "please specify wss_port\n");
+ return 0;
+ case 0x530:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
+ break;
+ case 0x604:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
+ break;
+ case 0xe80:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
+ break;
+ case 0xf40:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
+ break;
+ default:
+ dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
+ return 0;
+ }
+
+ switch (irq[n]) {
+ case SNDRV_AUTO_IRQ:
+ dev_err(dev, "please specify irq\n");
+ return 0;
+ case 7:
+ wss_config[n] |= WSS_CONFIG_IRQ_7;
+ break;
+ case 2:
+ irq[n] = 9;
+ case 9:
+ wss_config[n] |= WSS_CONFIG_IRQ_9;
+ break;
+ case 10:
+ wss_config[n] |= WSS_CONFIG_IRQ_10;
+ break;
+ case 11:
+ wss_config[n] |= WSS_CONFIG_IRQ_11;
+ break;
+ default:
+ dev_err(dev, "invalid IRQ %d\n", irq[n]);
+ return 0;
+ }
+
+ switch (dma1[n]) {
+ case SNDRV_AUTO_DMA:
+ dev_err(dev, "please specify dma1\n");
+ return 0;
+ case 0:
+ wss_config[n] |= WSS_CONFIG_DMA_0;
+ break;
+ case 1:
+ wss_config[n] |= WSS_CONFIG_DMA_1;
+ break;
+ case 3:
+ wss_config[n] |= WSS_CONFIG_DMA_3;
+ break;
+ default:
+ dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
+ return 0;
+ }
+
+ if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
+ dma2[n] = -1;
+ goto mpu;
+ }
+
+ wss_config[n] |= WSS_CONFIG_DUPLEX;
+ switch (dma2[n]) {
+ case 0:
+ break;
+ case 1:
+ if (dma1[n] == 0)
+ break;
+ default:
+ dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
+ return 0;
+ }
+
+mpu:
+ switch (mpu_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
+ mpu_port[n] = -1;
+ goto fm;
+ case 0x300:
+ config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
+ break;
+ case 0x330:
+ config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
+ break;
+ default:
+ dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
+ return 0;
+ }
+
+ switch (mpu_irq[n]) {
+ case SNDRV_AUTO_IRQ:
+ dev_warn(dev, "mpu_irq not specified: using polling mode\n");
+ mpu_irq[n] = -1;
+ break;
+ case 2:
+ mpu_irq[n] = 9;
+ case 9:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_2;
+ break;
+#ifdef AZT1605
+ case 3:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_3;
+ break;
+#endif
+ case 5:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_5;
+ break;
+ case 7:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_7;
+ break;
+#ifdef AZT2316
+ case 10:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_10;
+ break;
+#endif
+ default:
+ dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
+ return 0;
+ }
+
+ if (mpu_irq[n] == irq[n]) {
+ dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
+ return 0;
+ }
+
+fm:
+ switch (fm_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_warn(dev, "fm_port not specified: not using OPL3\n");
+ fm_port[n] = -1;
+ break;
+ case 0x388:
+ break;
+ default:
+ dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
+ return 0;
+ }
+
+ config[n] |= GALAXY_CONFIG_GAME_ENABLE;
+ return 1;
+}
+
+static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+{
+ u8 major;
+ u8 minor;
+ int err;
+
+ err = dsp_reset(galaxy->port);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_version(galaxy->port, &major, &minor);
+ if (err < 0)
+ return err;
+
+ if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
+ return -ENODEV;
+
+ err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
+ if (err < 0)
+ return err;
+
+ err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(galaxy->port, type);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+{
+ int err;
+
+ err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
+ if (err < 0)
+ return err;
+
+ err = dsp_command(galaxy->port, mode);
+ if (err < 0)
+ return err;
+
+#ifdef AZT1605
+ /*
+ * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
+ */
+ err = dsp_reset(galaxy->port);
+ if (err < 0)
+ return err;
+#endif
+
+ return 0;
+}
+
+static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
+{
+ u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
+ int i;
+
+ iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
+ for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
+ iowrite8(config, galaxy->config_port + i);
+ config >>= 8;
+ }
+ iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
+ msleep(10);
+}
+
+static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+{
+ int i;
+
+ for (i = GALAXY_CONFIG_SIZE; i; i--) {
+ u8 tmp = ioread8(galaxy->config_port + i - 1);
+ galaxy->config = (galaxy->config << 8) | tmp;
+ }
+ config |= galaxy->config & GALAXY_CONFIG_MASK;
+ galaxy_set_config(galaxy, config);
+}
+
+static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+{
+ int err;
+
+ err = wss_detect(galaxy->wss_port);
+ if (err < 0)
+ return err;
+
+ wss_set_config(galaxy->wss_port, wss_config);
+
+ err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void snd_galaxy_free(struct snd_card *card)
+{
+ struct snd_galaxy *galaxy = card->private_data;
+
+ if (galaxy->wss_port) {
+ wss_set_config(galaxy->wss_port, 0);
+ ioport_unmap(galaxy->wss_port);
+ release_and_free_resource(galaxy->res_wss_port);
+ }
+ if (galaxy->config_port) {
+ galaxy_set_config(galaxy, galaxy->config);
+ ioport_unmap(galaxy->config_port);
+ release_and_free_resource(galaxy->res_config_port);
+ }
+ if (galaxy->port) {
+ ioport_unmap(galaxy->port);
+ release_and_free_resource(galaxy->res_port);
+ }
+}
+
+static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+{
+ struct snd_galaxy *galaxy;
+ struct snd_wss *chip;
+ struct snd_card *card;
+ u8 type;
+ int err;
+
+ err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
+ &card);
+ if (err < 0)
+ return err;
+
+ snd_card_set_dev(card, dev);
+
+ card->private_free = snd_galaxy_free;
+ galaxy = card->private_data;
+
+ galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+ if (!galaxy->res_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
+ port[n] + 15);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->port = ioport_map(port[n], 16);
+
+ err = galaxy_init(galaxy, &type);
+ if (err < 0) {
+ dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
+ goto error;
+ }
+ dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
+
+ galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
+ 16, DRV_NAME);
+ if (!galaxy->res_config_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n",
+ port[n] + GALAXY_PORT_CONFIG,
+ port[n] + GALAXY_PORT_CONFIG + 15);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
+
+ galaxy_config(galaxy, config[n]);
+
+ galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+ if (!galaxy->res_wss_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
+ wss_port[n] + 3);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->wss_port = ioport_map(wss_port[n], 4);
+
+ err = galaxy_wss_config(galaxy, wss_config[n]);
+ if (err < 0) {
+ dev_err(dev, "could not configure WSS\n");
+ goto error;
+ }
+
+ strcpy(card->driver, DRV_NAME);
+ strcpy(card->shortname, DRV_NAME);
+ sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
+ card->shortname, port[n], wss_port[n], irq[n], dma1[n],
+ dma2[n]);
+
+ err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
+ dma2[n], WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_pcm(chip, 0, NULL);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_mixer(chip);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_timer(chip, 0, NULL);
+ if (err < 0)
+ goto error;
+
+ if (mpu_port[n] >= 0) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port[n], 0, mpu_irq[n],
+ IRQF_DISABLED, NULL);
+ if (err < 0)
+ goto error;
+ }
+
+ if (fm_port[n] >= 0) {
+ struct snd_opl3 *opl3;
+
+ err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
+ OPL3_HW_AUTO, 0, &opl3);
+ if (err < 0) {
+ dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
+ goto error;
+ }
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
+ goto error;
+
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(dev, card);
+ return 0;
+
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+{
+ snd_card_free(dev_get_drvdata(dev));
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static struct isa_driver snd_galaxy_driver = {
+ .match = snd_galaxy_match,
+ .probe = snd_galaxy_probe,
+ .remove = __devexit_p(snd_galaxy_remove),
+
+ .driver = {
+ .name = DEV_NAME
+ }
+};
+
+static int __init alsa_card_galaxy_init(void)
+{
+ return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
+}
+
+static void __exit alsa_card_galaxy_exit(void)
+{
+ isa_unregister_driver(&snd_galaxy_driver);
+}
+
+module_init(alsa_card_galaxy_init);
+module_exit(alsa_card_galaxy_exit);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f26eac8d8110..3e4a58b72913 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -191,7 +191,7 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
static void snd_gusmax_free(struct snd_card *card)
{
- struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data;
+ struct snd_gusmax *maxcard = card->private_data;
if (maxcard == NULL)
return;
@@ -219,7 +219,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
if (err < 0)
return err;
card->private_free = snd_gusmax_free;
- maxcard = (struct snd_gusmax *)card->private_data;
+ maxcard = card->private_data;
maxcard->card = card;
maxcard->irq = -1;
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 81284a8fa0ce..2259e3f726a7 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -72,7 +72,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
static void snd_sb8_free(struct snd_card *card)
{
- struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+ struct snd_sb8 *acard = card->private_data;
if (acard == NULL)
return;
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
deleted file mode 100644
index 6fe27b9d9440..000000000000
--- a/sound/isa/sgalaxy.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Driver for Aztech Sound Galaxy cards
- * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
- *
- * I don't have documentation for this card, I based this driver on the
- * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
- *
- * 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/init.h>
-#include <linux/err.h>
-#include <linux/isa.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <asm/dma.h>
-#include <sound/core.h>
-#include <sound/sb.h>
-#include <sound/wss.h>
-#include <sound/control.h>
-#define SNDRV_LEGACY_FIND_FREE_IRQ
-#define SNDRV_LEGACY_FIND_FREE_DMA
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
-MODULE_DESCRIPTION("Aztech Sound Galaxy");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}");
-
-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; /* Enable this card */
-static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */
-static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */
-static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */
-static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard.");
-module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver.");
-module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver.");
-
-#define SGALAXY_AUXC_LEFT 18
-#define SGALAXY_AUXC_RIGHT 19
-
-#define PFX "sgalaxy: "
-
-/*
-
- */
-
-#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
-
-/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */
-/* short time we actually need it.. */
-
-static int snd_sgalaxy_sbdsp_reset(unsigned long port)
-{
- int i;
-
- outb(1, SBP1(port, RESET));
- udelay(10);
- outb(0, SBP1(port, RESET));
- udelay(30);
- for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
- if (inb(SBP1(port, READ)) != 0xaa) {
- snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
- return -ENODEV;
- }
- return 0;
-}
-
-static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port,
- unsigned char val)
-{
- int i;
-
- for (i = 10000; i; i--)
- if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
- outb(val, SBP1(port, COMMAND));
- return 1;
- }
-
- return 0;
-}
-
-static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id)
-{
- return IRQ_NONE;
-}
-
-static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
-{
- static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1,
- 0x10, 0x18, 0x20, -1, -1, -1, -1};
- static int dma_bits[] = {1, 2, 0, 3};
- int tmp, tmp1;
-
- if ((tmp = inb(port + 3)) == 0xff)
- {
- snd_printdd("I/O address dead (0x%lx)\n", port);
- return 0;
- }
-#if 0
- snd_printdd("WSS signature = 0x%x\n", tmp);
-#endif
-
- if ((tmp & 0x3f) != 0x04 &&
- (tmp & 0x3f) != 0x0f &&
- (tmp & 0x3f) != 0x00) {
- snd_printdd("No WSS signature detected on port 0x%lx\n",
- port + 3);
- return 0;
- }
-
-#if 0
- snd_printdd(PFX "setting up IRQ/DMA for WSS\n");
-#endif
-
- /* initialize IRQ for WSS codec */
- tmp = interrupt_bits[irq % 16];
- if (tmp < 0)
- return -EINVAL;
-
- if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
- snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq);
- return -EIO;
- }
-
- outb(tmp | 0x40, port);
- tmp1 = dma_bits[dma % 4];
- outb(tmp | tmp1, port);
-
- free_irq(irq, NULL);
-
- return 0;
-}
-
-static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
-{
-#if 0
- snd_printdd(PFX "switching to WSS mode\n");
-#endif
-
- /* switch to WSS mode */
- snd_sgalaxy_sbdsp_reset(sbport[dev]);
-
- snd_sgalaxy_sbdsp_command(sbport[dev], 9);
- snd_sgalaxy_sbdsp_command(sbport[dev], 0);
-
- udelay(400);
- return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
-}
-
-static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
-WSS_DOUBLE("Aux Playback Switch", 0,
- SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
- SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
-};
-
-static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
-{
- struct snd_card *card = chip->card;
- struct snd_ctl_elem_id id1, id2;
- unsigned int idx;
- int err;
-
- memset(&id1, 0, sizeof(id1));
- memset(&id2, 0, sizeof(id2));
- id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- /* reassign AUX0 to LINE */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "Line Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Line Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- /* reassign AUX1 to FM */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "FM Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "FM Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- /* build AUX2 input */
- for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev)
-{
- if (!enable[dev])
- return 0;
- if (sbport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify SB port\n");
- return 0;
- }
- if (wssport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify WSS port\n");
- return 0;
- }
- return 1;
-}
-
-static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
-{
- static int possible_irqs[] = {7, 9, 10, 11, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
- int err, xirq, xdma1;
- struct snd_card *card;
- struct snd_wss *chip;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (err < 0)
- return err;
-
- xirq = irq[dev];
- if (xirq == SNDRV_AUTO_IRQ) {
- if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto _err;
- }
- }
- xdma1 = dma1[dev];
- if (xdma1 == SNDRV_AUTO_DMA) {
- if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
- err = -EBUSY;
- goto _err;
- }
- }
-
- if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
- goto _err;
-
- err = snd_wss_create(card, wssport[dev] + 4, -1,
- xirq, xdma1, -1,
- WSS_HW_DETECT, 0, &chip);
- if (err < 0)
- goto _err;
- card->private_data = chip;
-
- err = snd_wss_pcm(chip, 0, NULL);
- if (err < 0) {
- snd_printdd(PFX "error creating new WSS PCM device\n");
- goto _err;
- }
- err = snd_wss_mixer(chip);
- if (err < 0) {
- snd_printdd(PFX "error creating new WSS mixer\n");
- goto _err;
- }
- if ((err = snd_sgalaxy_mixer(chip)) < 0) {
- snd_printdd(PFX "the mixer rewrite failed\n");
- goto _err;
- }
-
- strcpy(card->driver, "Sound Galaxy");
- strcpy(card->shortname, "Sound Galaxy");
- sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
- wssport[dev], xirq, xdma1);
-
- snd_card_set_dev(card, devptr);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
-
- dev_set_drvdata(devptr, card);
- return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
- pm_message_t state)
-{
- struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_wss *chip = card->private_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- chip->suspend(chip);
- return 0;
-}
-
-static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
-{
- struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_wss *chip = card->private_data;
-
- chip->resume(chip);
- snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
- snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
-}
-#endif
-
-#define DEV_NAME "sgalaxy"
-
-static struct isa_driver snd_sgalaxy_driver = {
- .match = snd_sgalaxy_match,
- .probe = snd_sgalaxy_probe,
- .remove = __devexit_p(snd_sgalaxy_remove),
-#ifdef CONFIG_PM
- .suspend = snd_sgalaxy_suspend,
- .resume = snd_sgalaxy_resume,
-#endif
- .driver = {
- .name = DEV_NAME
- },
-};
-
-static int __init alsa_card_sgalaxy_init(void)
-{
- return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sgalaxy_exit(void)
-{
- isa_unregister_driver(&snd_sgalaxy_driver);
-}
-
-module_init(alsa_card_sgalaxy_init)
-module_exit(alsa_card_sgalaxy_exit)
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
index c6f2621221ba..8a12621d8b3a 100644
--- a/sound/oss/au1550_ac97.c
+++ b/sound/oss/au1550_ac97.c
@@ -171,7 +171,7 @@ au1550_delay(int msec)
static u16
rdcodec(struct ac97_codec *codec, u8 addr)
{
- struct au1550_state *s = (struct au1550_state *)codec->private_data;
+ struct au1550_state *s = codec->private_data;
unsigned long flags;
u32 cmd, val;
u16 data;
@@ -239,7 +239,7 @@ rdcodec(struct ac97_codec *codec, u8 addr)
static void
wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
{
- struct au1550_state *s = (struct au1550_state *)codec->private_data;
+ struct au1550_state *s = codec->private_data;
unsigned long flags;
u32 cmd, val;
int i;
@@ -820,7 +820,7 @@ mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
static long
au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct ac97_codec *codec = s->codec;
int ret;
@@ -1031,7 +1031,7 @@ copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
static ssize_t
au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db = &s->dma_adc;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
@@ -1111,7 +1111,7 @@ out2:
static ssize_t
au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db = &s->dma_dac;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
@@ -1211,7 +1211,7 @@ out2:
static unsigned int
au1550_poll(struct file *file, struct poll_table_struct *wait)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
unsigned long flags;
unsigned int mask = 0;
@@ -1250,7 +1250,7 @@ au1550_poll(struct file *file, struct poll_table_struct *wait)
static int
au1550_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db;
unsigned long size;
int ret = 0;
@@ -1342,7 +1342,7 @@ dma_count_done(struct dmabuf *db)
static int
au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
@@ -1868,7 +1868,7 @@ out2:
static int
au1550_release(struct inode *inode, struct file *file)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
lock_kernel();
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index e7a8cd058efb..12e34653b8a8 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -207,12 +207,12 @@ config SND_CMIPCI
config SND_OXYGEN_LIB
tristate
- select SND_PCM
- select SND_MPU401_UART
config SND_OXYGEN
tristate "C-Media 8788 (Oxygen)"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
help
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
@@ -581,6 +581,8 @@ config SND_HDSPM
config SND_HIFIER
tristate "TempoTec HiFier Fantasia"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
help
Say Y here to include support for the MediaTek/TempoTec HiFier
Fantasia sound card.
@@ -815,14 +817,17 @@ config SND_VIA82XX_MODEM
will be called snd-via82xx-modem.
config SND_VIRTUOSO
- tristate "Asus Virtuoso 100/200 (Xonar)"
+ tristate "Asus Virtuoso 66/100/200 (Xonar)"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
+ select SND_JACK if INPUT=y || INPUT=SND
help
Say Y here to include support for sound cards based on the
- Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
+ Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
Essence ST (Deluxe), and Essence STX.
- Support for the DS is experimental.
- Support for the HDAV1.3 (Deluxe) is very experimental.
+ Support for the HDAV1.3 (Deluxe) is incomplete; for the
+ HDAV1.3 Slim and Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 0a3d3d6e77b4..8e69620da20b 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1002,29 +1002,27 @@ snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
- snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+ unsigned int ptr, prev_ptr;
int channel = epcm->channel_id;
+ int timeout = 10;
if (!epcm->running)
return 0;
- ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
- ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
- ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
- if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
- ptr2 = bytes_to_frames(runtime, ptr1);
- ptr2+= (ptr4 >> 3) * runtime->period_size;
- ptr=ptr2;
- if (ptr >= runtime->buffer_size)
- ptr -= runtime->buffer_size;
- /*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
- "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
- ptr1, ptr2, ptr, (int)runtime->buffer_size,
- (int)runtime->period_size, (int)runtime->frame_bits,
- (int)runtime->rate);
- */
- return ptr;
+ prev_ptr = -1;
+ do {
+ ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+ ptr = (ptr >> 3) * runtime->period_size;
+ ptr += bytes_to_frames(runtime,
+ snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel));
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ if (prev_ptr == ptr)
+ return ptr;
+ prev_ptr = ptr;
+ } while (--timeout);
+ snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n");
+ return 0;
}
/* pointer_capture callback */
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 8578c70c61f2..bab564824efe 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -321,7 +321,7 @@ static struct snd_rawmidi_ops snd_emu10k1_midi_input =
static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
{
- struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
+ struct snd_emu10k1_midi *midi = rmidi->private_data;
midi->interrupt = NULL;
midi->rmidi = NULL;
}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 14829210ef0b..08d81b873022 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -4372,6 +4372,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
}
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+ int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->num_inputs++;
+ }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+ int i, j;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ for (j = i + 1; j < cfg->num_inputs; j++) {
+ if (cfg->inputs[i].type > cfg->inputs[j].type) {
+ struct auto_pin_cfg_item tmp;
+ tmp = cfg->inputs[i];
+ cfg->inputs[i] = cfg->inputs[j];
+ cfg->inputs[j] = tmp;
+ }
+ }
+ }
+}
+
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
@@ -4385,7 +4413,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists.
*
- * The analog input pins are assigned to input_pins array.
+ * The analog input pins are assigned to inputs array.
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
* respectively.
*/
@@ -4398,6 +4426,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
memset(cfg, 0, sizeof(*cfg));
@@ -4468,33 +4497,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++;
break;
- case AC_JACK_MIC_IN: {
- int preferred, alt;
- if (loc == AC_JACK_LOC_FRONT ||
- (loc & 0x30) == AC_JACK_LOC_INTERNAL) {
- preferred = AUTO_PIN_FRONT_MIC;
- alt = AUTO_PIN_MIC;
- } else {
- preferred = AUTO_PIN_MIC;
- alt = AUTO_PIN_FRONT_MIC;
- }
- if (!cfg->input_pins[preferred])
- cfg->input_pins[preferred] = nid;
- else if (!cfg->input_pins[alt])
- cfg->input_pins[alt] = nid;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
break;
- }
case AC_JACK_LINE_IN:
- if (loc == AC_JACK_LOC_FRONT)
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
- else
- cfg->input_pins[AUTO_PIN_LINE] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
break;
case AC_JACK_CD:
- cfg->input_pins[AUTO_PIN_CD] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
break;
case AC_JACK_AUX:
- cfg->input_pins[AUTO_PIN_AUX] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
break;
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
@@ -4539,6 +4552,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
memmove(sequences_hp + i, sequences_hp + i + 1,
sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
}
+ memset(cfg->hp_pins + cfg->hp_outs, 0,
+ sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
}
/* sort by sequence */
@@ -4549,21 +4564,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
cfg->hp_outs);
- /* if we have only one mic, make it AUTO_PIN_MIC */
- if (!cfg->input_pins[AUTO_PIN_MIC] &&
- cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
- cfg->input_pins[AUTO_PIN_MIC] =
- cfg->input_pins[AUTO_PIN_FRONT_MIC];
- cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
- }
- /* ditto for line-in */
- if (!cfg->input_pins[AUTO_PIN_LINE] &&
- cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
- cfg->input_pins[AUTO_PIN_LINE] =
- cfg->input_pins[AUTO_PIN_FRONT_LINE];
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
- }
-
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
@@ -4602,6 +4602,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
break;
}
+ sort_autocfg_input_pins(cfg);
+
/*
* debug prints of the parsed results
*/
@@ -4621,14 +4623,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
- " cd=0x%x, aux=0x%x\n",
- cfg->input_pins[AUTO_PIN_MIC],
- cfg->input_pins[AUTO_PIN_FRONT_MIC],
- cfg->input_pins[AUTO_PIN_LINE],
- cfg->input_pins[AUTO_PIN_FRONT_LINE],
- cfg->input_pins[AUTO_PIN_CD],
- cfg->input_pins[AUTO_PIN_AUX]);
+ snd_printd(" inputs:");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ snd_printdd(" %s=0x%x",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ snd_printd("\n");
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
@@ -4636,11 +4637,174 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-/* labels for input pins */
-const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+enum {
+ MIC_ATTR_INT,
+ MIC_ATTR_DOCK,
+ MIC_ATTR_NORMAL,
+ MIC_ATTR_FRONT,
+ MIC_ATTR_REAR,
};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+
+static int get_mic_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_FIXED ||
+ (loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return MIC_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return MIC_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return MIC_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return MIC_ATTR_FRONT;
+ return MIC_ATTR_NORMAL;
+}
+
+enum {
+ LINE_ATTR_DOCK,
+ LINE_ATTR_NORMAL,
+};
+
+static int get_line_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ if ((loc & 0xf0) == AC_JACK_LOC_SEPARATE)
+ return LINE_ATTR_DOCK;
+ return LINE_ATTR_NORMAL;
+}
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location)
+{
+ unsigned int def_conf;
+ static const char *mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ };
+ static const char *line_names[] = {
+ "Dock Line", "Line",
+ };
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (!check_location)
+ return "Mic";
+ return mic_names[get_mic_pin_attr(def_conf)];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ return line_names[get_line_pin_attr(def_conf)];
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ default:
+ return "Misc";
+ }
+}
+EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = get_mic_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= MIC_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = get_mic_pin_attr(defc);
+ if (attr2 >= MIC_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label. This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx)
+{
+ int i, label_idx = 0;
+ if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+ snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < imux->num_items; i++) {
+ if (!strncmp(label, imux->items[i].label, strlen(label)))
+ label_idx++;
+ }
+ if (type_idx)
+ *type_idx = label_idx;
+ if (label_idx > 0)
+ snprintf(imux->items[imux->num_items].label,
+ sizeof(imux->items[imux->num_items].label),
+ "%s %d", label, label_idx);
+ else
+ strlcpy(imux->items[imux->num_items].label, label,
+ sizeof(imux->items[imux->num_items].label));
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
#ifdef CONFIG_PM
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5ea21285ee1f..fb0582f8d725 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -61,7 +61,6 @@ struct hda_gspec {
struct hda_gnode *cap_vol_node; /* Node for capture volume */
unsigned int cur_cap_src; /* current capture source */
struct hda_input_mux input_mux;
- char cap_labels[HDA_MAX_NUM_INPUTS][16];
unsigned int def_amp_in_caps;
unsigned int def_amp_out_caps;
@@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
* returns 0 if not found, 1 if found, or a negative error code.
*/
static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node)
+ struct hda_gnode *node, int idx)
{
int i, err;
unsigned int pinctl;
- char *label;
const char *type;
if (node->checked)
@@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
child = hda_get_node(spec, node->conn_list[i]);
if (! child)
continue;
- err = parse_adc_sub_nodes(codec, spec, child);
+ err = parse_adc_sub_nodes(codec, spec, child, idx);
if (err < 0)
return err;
if (err > 0) {
@@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 0;
type = "Input";
}
- label = spec->cap_labels[spec->input_mux.num_items];
- strcpy(label, type);
- spec->input_mux.items[spec->input_mux.num_items].label = label;
+ snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
/* unmute the PIN external input */
unmute_input(codec, node, 0); /* index = 0? */
@@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */
}
-/* add a capture source element */
-static void add_cap_src(struct hda_gspec *spec, int idx)
-{
- struct hda_input_mux_item *csrc;
- char *buf;
- int num, ocap;
-
- num = spec->input_mux.num_items;
- csrc = &spec->input_mux.items[num];
- buf = spec->cap_labels[num];
- for (ocap = 0; ocap < num; ocap++) {
- if (! strcmp(buf, spec->cap_labels[ocap])) {
- /* same label already exists,
- * put the index number to be unique
- */
- sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
- break;
- }
- }
- csrc->index = idx;
- spec->input_mux.num_items++;
-}
-
/*
* parse input
*/
@@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
/* ... then check the rests, more complicated connections */
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1053fff4bd0a..34940a079051 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -126,6 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH10},"
"{Intel, PCH},"
"{Intel, CPT},"
+ "{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
@@ -2749,6 +2750,8 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
+ /* PBG */
+ { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
/* ATI SB 450/600 */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 28ab4aead48f..6943efc78f66 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -215,7 +215,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
*/
#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
- const char *label;
+ char label[32];
unsigned int index;
};
struct hda_input_mux {
@@ -366,9 +366,7 @@ struct hda_bus_unsolicited {
enum {
AUTO_PIN_MIC,
- AUTO_PIN_FRONT_MIC,
- AUTO_PIN_LINE,
- AUTO_PIN_FRONT_LINE,
+ AUTO_PIN_LINE_IN,
AUTO_PIN_CD,
AUTO_PIN_AUX,
AUTO_PIN_LAST
@@ -380,9 +378,22 @@ enum {
AUTO_PIN_HP_OUT
};
-extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
-
#define AUTO_CFG_MAX_OUTS 5
+#define AUTO_CFG_MAX_INS 8
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location);
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_index_ret);
struct auto_pin_cfg {
int line_outs;
@@ -393,7 +404,8 @@ struct auto_pin_cfg {
int hp_outs;
int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t input_pins[AUTO_PIN_LAST];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
int dig_outs;
hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin;
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index b697fd2a6f8b..05db1cfcd8b8 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -2880,7 +2880,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int boost)
+ const char *ctlname, int ctlidx, int boost)
{
char name[32];
int err, idx;
@@ -2909,25 +2909,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
}
/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
+ struct ad198x_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
- int i, err;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- i <= AUTO_PIN_FRONT_MIC);
+ int i, err, type, type_idx;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label,
+ ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+ &type_idx);
+ err = new_analog_input(spec, cfg->inputs[i].pin,
+ label, type_idx,
+ type == AUTO_PIN_MIC);
if (err < 0)
return err;
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
- imux->num_items++;
}
- imux->items[imux->num_items].label = "Mix";
- imux->items[imux->num_items].index = 9;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Mix", 9, NULL);
if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
"Analog Mix Playback Volume",
@@ -2994,12 +2996,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
static void ad1988_auto_init_analog_input(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int i, idx;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (! nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
switch (nid) {
case 0x15: /* port-C */
snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3009,7 +3010,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
break;
}
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
if (nid != AD1988_PIN_CD_NID)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -3040,7 +3041,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
"Speaker")) < 0 ||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index af478019088e..cca11fdd3d79 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
spec->dig_in = nid;
continue;
}
- for (j = 0; j < AUTO_PIN_LAST; j++)
- if (cfg->input_pins[j] == pin)
+ for (j = 0; j < cfg->num_inputs; j++)
+ if (cfg->inputs[j].pin == pin)
break;
- if (j >= AUTO_PIN_LAST)
+ if (j >= cfg->num_inputs)
continue;
spec->input_pins[n] = pin;
- spec->input_labels[n] = auto_pin_cfg_labels[j];
+ spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
spec->adcs[n] = nid;
n++;
}
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 488fd9ade1ba..ae75283a5583 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -329,7 +329,7 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->input_pins[idx];
+ hda_nid_t pin = cfg->inputs[idx].pin;
unsigned int val = snd_hda_query_pin_caps(codec, pin);
if (!(val & AC_PINCAP_PRES_DETECT))
return 0;
@@ -424,10 +424,8 @@ static int parse_input(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
spec->input_idx[spec->num_inputs] = i;
spec->capsrc_idx[i] = spec->num_inputs++;
spec->cur_input = i;
@@ -438,16 +436,17 @@ static int parse_input(struct hda_codec *codec)
/* check whether the automatic mic switch is available */
if (spec->num_inputs == 2 &&
- spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
- if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ cfg->inputs[0].type == AUTO_PIN_MIC &&
+ cfg->inputs[1].type == AUTO_PIN_MIC) {
+ if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+ if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_FRONT_MIC;
+ spec->automic_idx = 0;
}
} else {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ if (is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_MIC;
+ spec->automic_idx = 1;
}
}
}
@@ -674,6 +673,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -682,7 +682,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item >= spec->num_inputs)
uinfo->value.enumerated.item = spec->num_inputs - 1;
idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+ strcpy(uinfo->value.enumerated.name,
+ hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
return 0;
}
@@ -853,15 +854,12 @@ static void cs_automic(struct hda_codec *codec)
hda_nid_t nid;
unsigned int present;
- nid = cfg->input_pins[spec->automic_idx];
+ nid = cfg->inputs[spec->automic_idx].pin;
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) ?
- AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
- change_cur_input(codec, imic, 0);
- }
+ else
+ change_cur_input(codec, !spec->automic_idx, 0);
}
/*
@@ -918,14 +916,14 @@ static void init_input(struct hda_codec *codec)
unsigned int coef;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
unsigned int ctl;
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin || !spec->adc_nid[i])
+ hda_nid_t pin = cfg->inputs[i].pin;
+ if (!spec->adc_nid[i])
continue;
/* set appropriate pin control and mute first */
ctl = PIN_IN;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
unsigned int caps = snd_hda_query_pin_caps(codec, pin);
caps >>= AC_PINCAP_VREF_SHIFT;
if (caps & AC_PINCAP_VREF_80)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 71f9d6475b09..972e7c453b3d 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -3092,6 +3092,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
+ SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index bcbf9160ed81..9c2c19c8b059 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -846,7 +846,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
{
unsigned int val = PIN_IN;
- if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+ if (auto_pin_type == AUTO_PIN_MIC) {
unsigned int pincap;
unsigned int oldval;
oldval = snd_hda_codec_read(codec, nid, 0,
@@ -990,25 +990,46 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-static void alc_automute_pin(struct hda_codec *codec)
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
{
struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
+ unsigned int mute;
+ hda_nid_t nid;
int i;
- if (!nid)
- return;
- spec->jack_present = snd_hda_jack_detect(codec, nid);
+ spec->jack_present = 0;
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+ nid = spec->autocfg.hp_pins[i];
+ if (!nid)
+ break;
+ if (snd_hda_jack_detect(codec, nid)) {
+ spec->jack_present = 1;
+ break;
+ }
+ }
+
+ mute = spec->jack_present ? HDA_AMP_MUTE : 0;
+ /* Toggle internal speakers muting */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
if (!nid)
break;
- snd_hda_codec_write(codec, nid, 0,
+ if (pinctl) {
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->jack_present ? 0 : PIN_OUT);
+ } else {
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
}
}
+static void alc_automute_pin(struct hda_codec *codec)
+{
+ alc_automute_speaker(codec, 1);
+}
+
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid)
{
@@ -1236,24 +1257,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
static void alc_init_auto_hp(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
- if (!spec->autocfg.hp_pins[0])
- return;
+ if (!cfg->hp_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ return;
+ }
- if (!spec->autocfg.speaker_pins[0]) {
- if (spec->autocfg.line_out_pins[0] &&
- spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- spec->autocfg.speaker_pins[0] =
- spec->autocfg.line_out_pins[0];
- else
+ if (!cfg->speaker_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
return;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
+ }
+
+ if (!cfg->hp_pins[0]) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
}
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- spec->autocfg.hp_pins[0]);
- snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+ for (i = 0; i < cfg->hp_outs; i++) {
+ snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+ cfg->hp_pins[i]);
+ snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | ALC880_HP_EVENT);
+ }
spec->unsol_event = alc_sku_unsol_event;
}
@@ -1265,16 +1297,14 @@ static void alc_init_auto_mic(struct hda_codec *codec)
int i;
/* there must be only two mic inputs exclusively */
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return;
fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
- hda_nid_t nid = cfg->input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
unsigned int defcfg;
- if (!nid)
- return;
defcfg = snd_hda_codec_get_pincfg(codec, nid);
switch (get_defcfg_connect(defcfg)) {
case AC_JACK_PORT_FIXED:
@@ -1713,31 +1743,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 mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- if (snd_hda_jack_detect(codec, nid)) {
- spec->jack_present = 1;
- break;
- }
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ alc_automute_speaker(codec, 0);
}
static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -4719,7 +4725,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
/* add dynamic controls */
static int add_control(struct alc_spec *spec, int type, const char *name,
- unsigned long val)
+ int cidx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -4731,6 +4737,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ knew->index = cidx;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
knew->private_value = val;
@@ -4739,17 +4746,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
static int add_control_with_pfx(struct alc_spec *spec, int type,
const char *pfx, const char *dir,
- const char *sfx, unsigned long val)
+ const char *sfx, int cidx, unsigned long val)
{
char name[32];
snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, val);
+ return add_control(spec, type, name, cidx, 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 add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
@@ -4902,16 +4913,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname,
+ const char *ctlname, int ctlidx,
int idx, hda_nid_t mix_nid)
{
int err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+ err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+ err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -4932,20 +4943,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
{
struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
+ int i, err, idx, type, type_idx = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t pin;
+ const char *label;
- pin = cfg->input_pins[i];
+ pin = cfg->inputs[i].pin;
if (!alc_is_input_pin(codec, pin))
continue;
+ type = cfg->inputs[i].type;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
if (mixer) {
idx = get_connection_index(codec, mixer, pin);
if (idx >= 0) {
err = new_analog_input(spec, pin,
- auto_pin_cfg_labels[i],
+ label, type_idx,
idx, mixer);
if (err < 0)
return err;
@@ -4957,12 +4975,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
idx = get_connection_index(codec, cap1, pin);
if (idx < 0 && cap2)
idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0) {
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ if (idx >= 0)
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
@@ -5034,10 +5048,11 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
static void alc880_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC880_PIN_CD_NID &&
@@ -5204,19 +5219,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
static void fixup_single_adc(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin = 0;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
/* search for the input pin; there must be only one */
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (spec->autocfg.input_pins[i]) {
- pin = spec->autocfg.input_pins[i];
- break;
- }
- }
- if (!pin)
+ if (cfg->num_inputs != 1)
return;
- i = init_capsrc_for_pin(codec, pin);
+ i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
if (i >= 0) {
/* use only this ADC */
if (spec->capsrc_nids)
@@ -5269,6 +5278,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
int num_nids)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int n;
hda_nid_t fallback_adc = 0, fallback_cap = 0;
@@ -5294,10 +5304,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
fallback_adc = adc;
fallback_cap = cap;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
for (j = 0; j < nconns; j++) {
if (conn[j] == nid)
break;
@@ -5305,7 +5313,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
if (j >= nconns)
break;
}
- if (i >= AUTO_PIN_LAST) {
+ if (i >= cfg->num_inputs) {
int num_adcs = spec->num_adc_nids;
spec->private_adc_nids[num_adcs] = adc;
spec->private_capsrc_nids[num_adcs] = cap;
@@ -6673,10 +6681,11 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
static void alc260_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x12) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC260_PIN_CD_NID &&
@@ -6800,14 +6809,12 @@ enum {
PINFIX_HP_DC5750,
};
-static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
- { 0x11, 0x90130110 }, /* speaker */
- { }
-};
-
static const struct alc_fixup alc260_fixups[] = {
[PINFIX_HP_DC5750] = {
- .pins = alc260_hp_dc5750_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
},
};
@@ -10453,24 +10460,20 @@ enum {
PINFIX_PB_M5210,
};
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
-};
-
-static const struct hda_verb pb_m5210_verbs[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
-};
-
static const struct alc_fixup alc882_fixups[] = {
[PINFIX_ABIT_AW9D_MAX] = {
- .pins = alc882_abit_aw9d_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+ }
},
[PINFIX_PB_M5210] = {
- .verbs = pb_m5210_verbs
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
},
};
@@ -10545,12 +10548,11 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
static void alc882_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
alc_set_input_pin(codec, nid, i);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, nid, 0,
@@ -10613,24 +10615,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
static int alc_auto_add_mic_boost(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
hda_nid_t nid;
- nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Mic Boost",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Mic Boost",
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type > AUTO_PIN_MIC)
+ break;
+ nid = cfg->inputs[i].pin;
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ char label[32];
+ snprintf(label, sizeof(label), "%s Boost",
+ hda_get_autocfg_input_label(codec, cfg, i));
+ err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
@@ -11821,7 +11822,7 @@ 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)
+ const char *pfx, int *vbits, int idx)
{
unsigned long val;
int vbit;
@@ -11836,11 +11837,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
+ return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
}
static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx)
+ const char *pfx, int idx)
{
unsigned long val;
@@ -11850,7 +11851,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
+ return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
}
/* add playback controls from the parsed DAC table */
@@ -11859,7 +11860,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
{
const char *pfx;
int vbits;
- int err;
+ int i, err;
spec->multiout.num_dacs = 1; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
@@ -11869,39 +11870,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "Master";
else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker";
+ else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ pfx = "Headphone";
else
pfx = "Front";
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
+ "Headphone", i);
+ if (err < 0)
+ return err;
+ }
+ }
vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
alc262_check_volbit(cfg->speaker_pins[0]) |
alc262_check_volbit(cfg->hp_pins[0]);
if (vbits == 1 || vbits == 2)
pfx = "Master"; /* only one mixer is used */
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
vbits = 0;
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
- &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
- &vbits);
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
+ &vbits, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
+ "Headphone", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -12189,6 +12203,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
{}
};
+/*
+ * Pin config fixes
+ */
+enum {
+ PINFIX_FSC_H270,
+};
+
+static const struct alc_fixup alc262_fixups[] = {
+ [PINFIX_FSC_H270] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
+ },
+ [PINFIX_PB_M5210] = {
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
+ },
+};
+
+static struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+ {}
+};
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc262_loopbacks alc880_loopbacks
@@ -12612,6 +12655,9 @@ static int patch_alc262(struct hda_codec *codec)
board_config = ALC262_AUTO;
}
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
+
if (board_config == ALC262_AUTO) {
/* automatic parse from the BIOS config */
err = alc262_parse_auto_config(codec);
@@ -12680,6 +12726,9 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->no_analog && has_cdefine_beep(codec))
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
+
spec->vmaster_nid = 0x0c;
codec->patch_ops = alc_patch_ops;
@@ -14455,14 +14504,12 @@ enum {
ALC269_FIXUP_SONY_VAIO,
};
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
- {}
-};
-
static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_SONY_VAIO] = {
- .verbs = alc269_sony_vaio_fixup_verbs
+ .verbs = (const struct hda_verb[]) {
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+ {}
+ }
},
};
@@ -15586,10 +15633,11 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
static void alc861_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x0c && nid <= 0x11)
alc_set_input_pin(codec, nid, i);
}
@@ -15820,15 +15868,13 @@ 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
+ .pins = (const struct alc_pincfg[]) {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+ }
},
};
@@ -16580,10 +16626,11 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC861VD_PIN_CD_NID &&
@@ -16795,16 +16842,14 @@ enum {
};
/* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
-};
-
static const struct alc_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .verbs = alc660vd_fix_asus_gpio1_verbs,
+ .verbs = (const struct hda_verb[]) {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+ { }
+ }
},
};
@@ -18818,10 +18863,11 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
static void alc662_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC662_PIN_CD_NID &&
@@ -18915,6 +18961,26 @@ static void alc662_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
+enum {
+ ALC662_FIXUP_IDEAPAD,
+};
+
+static const struct alc_fixup alc662_fixups[] = {
+ [ALC662_FIXUP_IDEAPAD] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x17, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+};
+
+static struct snd_pci_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ {}
+};
+
+
+
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -18947,6 +19013,7 @@ static int patch_alc662(struct hda_codec *codec)
}
if (board_config == ALC662_AUTO) {
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
/* automatic parse from the BIOS config */
err = alc662_parse_auto_config(codec);
if (err < 0) {
@@ -19005,8 +19072,11 @@ static int patch_alc662(struct hda_codec *codec)
spec->vmaster_nid = 0x02;
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO)
+ if (board_config == ALC662_AUTO) {
spec->init_hook = alc662_auto_init;
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+ }
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc662_loopbacks;
@@ -19050,6 +19120,39 @@ static hda_nid_t alc680_adc_nids[3] = {
/*
* Analog capture ADC cgange
*/
+static void alc680_rec_autoswitch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int pin_found = 0;
+ int type_found = AUTO_PIN_LAST;
+ hda_nid_t nid;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (!(snd_hda_query_pin_caps(codec, nid) &
+ AC_PINCAP_PRES_DETECT))
+ continue;
+ if (snd_hda_jack_detect(codec, nid)) {
+ if (cfg->inputs[i].type < type_found) {
+ type_found = cfg->inputs[i].type;
+ pin_found = nid;
+ }
+ }
+ }
+
+ nid = 0x07;
+ if (pin_found)
+ snd_hda_get_connections(codec, pin_found, &nid, 1);
+
+ if (nid != spec->cur_adc)
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = nid;
+ snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+}
+
static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -19057,24 +19160,12 @@ static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int pre_mic, pre_line;
-
- pre_mic = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
- pre_line = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_LINE]);
+ spec->cur_adc = 0x07;
spec->cur_adc_stream_tag = stream_tag;
spec->cur_adc_format = format;
- if (pre_mic || pre_line) {
- if (pre_mic)
- snd_hda_codec_setup_stream(codec, 0x08, stream_tag, 0,
- format);
- else
- snd_hda_codec_setup_stream(codec, 0x09, stream_tag, 0,
- format);
- } else
- snd_hda_codec_setup_stream(codec, 0x07, stream_tag, 0, format);
+ alc680_rec_autoswitch(codec);
return 0;
}
@@ -19160,6 +19251,7 @@ static struct hda_verb alc680_init_verbs[] = {
{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
{ }
};
@@ -19172,25 +19264,11 @@ static void alc680_base_setup(struct hda_codec *codec)
spec->autocfg.hp_pins[0] = 0x16;
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.input_pins[AUTO_PIN_MIC] = 0x18;
- spec->autocfg.input_pins[AUTO_PIN_LINE] = 0x19;
-}
-
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int present;
- hda_nid_t new_adc;
-
- present = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-
- new_adc = present ? 0x8 : 0x7;
- __snd_hda_codec_cleanup_stream(codec, !present ? 0x8 : 0x7, 1);
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
-
+ spec->autocfg.num_inputs = 2;
+ spec->autocfg.inputs[0].pin = 0x18;
+ spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+ spec->autocfg.inputs[1].pin = 0x19;
+ spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
}
static void alc680_unsol_event(struct hda_codec *codec,
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 95148e58026c..e4e7d43f911e 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -382,6 +382,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
0x03, 0x0c, 0x20, 0x40,
};
+#define STAC92HD83XXX_NUM_DMICS 2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+ 0x11, 0x20, 0
+};
+
#define STAC92HD83XXX_NUM_CAPS 2
static unsigned long stac92hd83xxx_capvols[] = {
HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@@ -1105,9 +1110,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
struct hda_input_mux *smux = &spec->private_smux;
/* check for mute support on SPDIF out */
if (wcaps & AC_WCAP_OUT_AMP) {
- smux->items[smux->num_items].label = "Off";
- smux->items[smux->num_items].index = 0;
- smux->num_items++;
+ snd_hda_add_imux_item(smux, "Off", 0, NULL);
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
@@ -1180,14 +1183,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- nid = cfg->input_pins[i];
- if (nid) {
- err = stac92xx_add_jack(codec, nid,
- SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
}
return 0;
@@ -2789,7 +2789,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
}
if (control) {
- strcpy(name, auto_pin_cfg_labels[idx]);
+ strcpy(name, hda_get_input_pin_label(codec, nid, 1));
return stac92xx_add_control(codec->spec, control,
strcat(name, " Jack Mode"), nid);
}
@@ -2821,41 +2821,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
unsigned int pincap;
+ int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+ nid = cfg->inputs[i].pin;
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
+ }
+ }
return 0;
}
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
- unsigned int mic_pin;
+ int i;
+ *dac = 0;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- mic_pin = AUTO_PIN_MIC;
- for (;;) {
- hda_nid_t nid = cfg->input_pins[mic_pin];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ if (pincap & AC_PINCAP_OUT) {
+ *dac = get_unassigned_dac(codec, nid);
+ if (*dac)
+ return nid;
+ }
}
- if (mic_pin == AUTO_PIN_MIC)
- mic_pin = AUTO_PIN_FRONT_MIC;
- else
- break;
}
return 0;
}
@@ -3002,17 +3010,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
}
}
/* add mic as output */
- nid = check_mic_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
+ nid = check_mic_out_switch(codec, &dac);
+ if (nid && dac) {
+ snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->mic_switch = nid;
+ add_spec_dacs(spec, dac);
}
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@@ -3202,13 +3207,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err;
}
- for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
- nid = cfg->input_pins[idx];
- if (nid) {
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
+ for (idx = 0; idx < cfg->num_inputs; idx++) {
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ break;
+ nid = cfg->inputs[idx].pin;
+ err = stac92xx_add_jack_mode_control(codec, nid, idx);
+ if (err < 0)
+ return err;
}
return 0;
@@ -3254,12 +3259,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
return -EINVAL;
- for (i = 0; i < num_cons; i++) {
- mono_mux->items[mono_mux->num_items].label =
- stac92xx_mono_labels[i];
- mono_mux->items[mono_mux->num_items].index = i;
- mono_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+ NULL);
return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
"Mono Mux", spec->mono_nid);
@@ -3384,11 +3386,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
if (!labels)
labels = stac92xx_spdif_labels;
- for (i = 0; i < num_cons; i++) {
- spdif_mux->items[spdif_mux->num_items].label = labels[i];
- spdif_mux->items[spdif_mux->num_items].index = i;
- spdif_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
return 0;
}
@@ -3415,7 +3414,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
/* create a volume assigned to the given pin (only if supported) */
/* return 1 if the volume control is created */
static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int direction)
+ const char *label, int idx, int direction)
{
unsigned int caps, nums;
char name[32];
@@ -3432,8 +3431,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
if (!nums)
return 0;
snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
if (err < 0)
return err;
return 1;
@@ -3446,27 +3445,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i, active_mics;
+ int err, i;
unsigned int def_conf;
- dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
- dimux->items[dimux->num_items].index = 0;
- dimux->num_items++;
-
- active_mics = 0;
- for (i = 0; i < spec->num_dmics; i++) {
- /* check the validity: sometimes it's a dead vendor-spec node */
- if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
- != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
- active_mics++;
- }
+ snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid;
- int index;
+ int index, type_idx;
const char *label;
nid = spec->dmic_nids[i];
@@ -3480,29 +3466,22 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
if (index < 0)
continue;
- if (active_mics == 1)
- label = "Digital Mic";
- else
- label = stac92xx_dmic_labels[dimux->num_items];
+ label = hda_get_input_pin_label(codec, nid, 1);
+ snd_hda_add_imux_item(dimux, label, index, &type_idx);
- err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+ err = create_elem_capture_vol(codec, nid, label, type_idx,
+ HDA_INPUT);
if (err < 0)
return err;
if (!err) {
err = create_elem_capture_vol(codec, nid, label,
- HDA_OUTPUT);
+ type_idx, HDA_OUTPUT);
if (err < 0)
return err;
}
- dimux->items[dimux->num_items].label = label;
- dimux->items[dimux->num_items].index = index;
- dimux->num_items++;
- if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
- imux->items[imux->num_items].label = label;
- imux->items[imux->num_items].index = index;
- imux->num_items++;
- }
+ if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1)
+ snd_hda_add_imux_item(imux, label, index, NULL);
}
return 0;
@@ -3540,10 +3519,11 @@ static int set_mic_route(struct hda_codec *codec,
int i;
mic->pin = pin;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (pin == cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
break;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ }
+ if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
/* analog pin */
i = get_connection_index(codec, spec->mux_nids[0], pin);
if (i < 0)
@@ -3577,13 +3557,13 @@ static int stac_check_auto_mic(struct hda_codec *codec)
hda_nid_t fixed, ext;
int i;
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return 0; /* must be exclusively mics */
}
fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (check_mic_pin(codec, cfg->inputs[i].pin, &fixed, &ext))
return 0;
for (i = 0; i < spec->num_dmics; i++)
if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
@@ -3604,13 +3584,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
int i, j;
+ const char *label;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- int index, err;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int index, err, type_idx;
- if (!nid)
- continue;
index = -1;
for (j = 0; j < spec->num_muxes; j++) {
index = get_connection_index(codec, spec->mux_nids[j],
@@ -3621,15 +3600,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
if (index < 0)
continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, index, &type_idx);
+
err = create_elem_capture_vol(codec, nid,
- auto_pin_cfg_labels[i],
+ label, type_idx,
HDA_INPUT);
if (err < 0)
return err;
-
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = index;
- imux->num_items++;
}
spec->num_analog_muxes = imux->num_items;
@@ -4304,37 +4282,34 @@ static int stac92xx_init(struct hda_codec *codec)
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->ext_mic.pin);
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- if (nid) {
- unsigned int pinctl, conf;
- if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int type = cfg->inputs[i].type;
+ unsigned int pinctl, conf;
+ if (type == AUTO_PIN_MIC) {
+ /* for mic pins, force to initialize */
+ pinctl = stac92xx_get_default_vref(codec, nid);
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ } else {
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* if PINCTL already set then skip */
+ /* Also, if both INPUT and OUTPUT are set,
+ * it must be a BIOS bug; need to override, too
+ */
+ if (!(pinctl & AC_PINCTL_IN_EN) ||
+ (pinctl & AC_PINCTL_OUT_EN)) {
+ pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid,
- pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid,
- STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
}
}
+ conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+ if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+ stac_issue_unsol_event(codec, nid);
+ }
}
for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@@ -4686,6 +4661,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
}
}
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int cfg;
+
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
+
+static int stac92xx_connected_ports(struct hda_codec *codec,
+ hda_nid_t *nids, int num_nids)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx, num;
+ unsigned int def_conf;
+
+ for (num = 0; num < num_nids; num++) {
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == nids[num])
+ break;
+ if (idx >= spec->num_pins)
+ break;
+ def_conf = stac_get_defcfg_connect(codec, idx);
+ if (def_conf == AC_JACK_PORT_NONE)
+ break;
+ }
+ return num;
+}
+
static void stac92xx_mic_detect(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -5316,6 +5321,8 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
spec->linear_tone_beep = 1;
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
spec->digbeep_nid = 0x21;
+ spec->dmic_nids = stac92hd83xxx_dmic_nids;
+ spec->dmux_nids = stac92hd83xxx_mux_nids;
spec->mux_nids = stac92hd83xxx_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
spec->adc_nids = stac92hd83xxx_adc_nids;
@@ -5361,9 +5368,13 @@ again:
case 0x111d76d4:
case 0x111d7605:
case 0x111d76d5:
+ case 0x111d76e7:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd83xxx_dmic_nids,
+ STAC92HD83XXX_NUM_DMICS);
break;
}
@@ -5422,36 +5433,6 @@ again:
return 0;
}
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
hda_nid_t dig0pin)
{
@@ -5590,7 +5571,7 @@ again:
case 0x111d76b5:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -5622,7 +5603,7 @@ again:
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS - 1);
break;
@@ -5636,7 +5617,7 @@ again:
default:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -6318,6 +6299,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ae3acb2b42d1..d1c3f8defc48 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
/* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
- unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+ int idx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
return 0;
}
+#define via_add_control(spec, type, name, val) \
+ __via_add_control(spec, type, name, 0, val)
+
static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
struct snd_kcontrol_new *tmpl)
{
@@ -494,18 +497,18 @@ 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, const char *ctlname,
- int idx, int mix_nid)
+ int type_idx, int idx, int mix_nid)
{
char name[32];
int err;
sprintf(name, "%s Playback Volume", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
static void via_auto_init_analog_input(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int ctl;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
-
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (spec->smart51_enabled && is_smart51_pins(spec, nid))
ctl = PIN_OUT;
- else if (i <= AUTO_PIN_FRONT_MIC)
+ else if (i == AUTO_PIN_MIC)
ctl = PIN_VREF50;
else
ctl = PIN_IN;
@@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
}
static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
{
- int res = 0;
- int index;
- for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
- if (pin == spec->autocfg.input_pins[index]) {
- res = 1;
- break;
- }
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
+ return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
}
- return res;
+ return 0;
}
static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int on = 1;
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
- int ctl =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if (ctl & AC_PINCTL_IN_EN
- && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int ctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
+ continue; /* ignore FMic for independent HP */
+ if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
+ on = 0;
}
*ucontrol->value.integer.value = on;
return 0;
@@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int out_in = *ucontrol->value.integer.value
? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int parm;
+
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
continue; /* don't retask FMic for independent HP */
- if (nid) {
- unsigned int parm = snd_hda_codec_read(
- codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S)
- snd_hda_codec_amp_stereo(
+
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ parm |= out_in;
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ parm);
+ if (out_in == AC_PINCTL_OUT_EN) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ if (spec->codec_type == VT1718S) {
+ snd_hda_codec_amp_stereo(
codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
HDA_AMP_UNMUTE);
}
- if (i == AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
if (spec->codec_type == VT1708S
|| spec->codec_type == VT1716S) {
/* input = index 1 (AOW3) */
@@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
static int via_smart51_build(struct via_spec *spec)
{
struct snd_kcontrol_new *knew;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
int i;
@@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
if (knew == NULL)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
knew = via_clone_control(spec, &via_smart51_mixer[1]);
if (knew == NULL)
return -ENOMEM;
knew->subdevice = nid;
+ break;
}
}
@@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
static const char *texts[] = { "OFF", "ON", NULL};
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
}
@@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ hda_nid_t cap_nid,
+ hda_nid_t pin_idxs[], int num_idxs)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
+ int i, err, idx, type, type_idx = 0;
/* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x24: /* CD */
- idx = 1;
+ for (idx = 0; idx < num_idxs; idx++) {
+ if (pin_idxs[idx] == 0xff) {
+ snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
break;
}
- err = via_new_analog_input(spec, labels[i], idx, 0x17);
+ }
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ for (idx = 0; idx < num_idxs; idx++)
+ if (pin_idxs[idx] == cfg->inputs[i].pin)
+ break;
+ if (idx >= num_idxs)
+ continue;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
if (err < 0)
return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
static struct hda_amp_list vt1708_loopbacks[] = {
{ 0x17, HDA_INPUT, 1 },
@@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
/* add jack detect on/off control */
@@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x23: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x18);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1709_parse_auto_config(struct hda_codec *codec)
@@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1708B_parse_auto_config(struct hda_codec *codec)
@@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
/* fill out digital output widgets; one for master and one for slave outputs */
@@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
imux = &spec->private_imux[1];
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
return 0;
}
/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x14: /* Mic */
- idx = 1;
- break;
-
- case 0x15: /* Line In */
- idx = 2;
- break;
-
- case 0x18: /* Front Mic */
- idx = 3;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x1A);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1702_parse_auto_config(struct hda_codec *codec)
@@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 1;
- break;
-
- case 0x2a: /* Line In */
- idx = 2;
- break;
-
- case 0x29: /* Front Mic */
- idx = 3;
- break;
-
- case 0x2c: /* CD */
- idx = 0;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1718S_parse_auto_config(struct hda_codec *codec)
@@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1716S_parse_auto_config(struct hda_codec *codec)
@@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
-
- case 0x2a: /* Line In */
- idx = 1;
- break;
-
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
+ int err;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 4;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
return 0;
}
@@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
+ int err;
- case 0x2a: /* Line In */
- idx = 1;
- break;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 6;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
return 0;
}
@@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index d216362626d0..712c1710f9a2 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -563,6 +563,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_MEDIASTATION:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
break;
@@ -635,6 +636,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA66:
@@ -734,6 +736,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
@@ -813,5 +816,12 @@ struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
+ {
+ .subvendor = ICE1712_SUBDEVICE_EDIROLDA2496,
+ .name = "Edirol DA2496",
+ .model = "da2496",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index f7f14df81f26..1a0ac6cd6501 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -34,7 +34,8 @@
"{MidiMan M Audio,Delta 410},"\
"{MidiMan M Audio,Audiophile 24/96},"\
"{Digigram,VX442},"\
- "{Lionstracs,Mediastation},"
+ "{Lionstracs,Mediastation},"\
+ "{Edirol,DA2496},"
#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6
@@ -47,6 +48,7 @@
#define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6
#define ICE1712_SUBDEVICE_VX442 0x12143cd6
#define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100
+#define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010
/* entry point */
extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 6bc3f91b7281..cdb873f5da50 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -638,7 +638,7 @@ static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
*/
static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
mutex_lock(&ice->gpio_mutex);
@@ -653,7 +653,7 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf
static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
@@ -676,7 +676,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 2a8e5cd8f2d8..e36ddb94c382 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -654,7 +654,7 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
static void stac9460_proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
/* registers 0x0 - 0x14 */
for (reg = 0; reg <= 0x15; reg++) {
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index b82c1cfa96f5..aceaaa036da6 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -25,16 +25,24 @@
* SPI 0 -> WM8766 (surround, center/LFE, back)
* SPI 1 -> WM8776 (front, input)
*
- * GPIO 4 <- headphone detect
- * GPIO 6 -> route input jack to input 1/2 (1/0)
- * GPIO 7 -> enable output to speakers
- * GPIO 8 -> enable output to speakers
+ * GPIO 4 <- headphone detect, 0 = plugged
+ * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
+ * GPIO 7 -> enable output to front L/R speaker channels
+ * GPIO 8 -> enable output to other speaker channels and front panel headphone
+ *
+ * WM8766:
+ *
+ * input 1 <- line
+ * input 2 <- mic
+ * input 3 <- front mic
+ * input 4 <- aux
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@@ -44,7 +52,8 @@
#define GPIO_DS_HP_DETECT 0x0010
#define GPIO_DS_INPUT_ROUTE 0x0040
-#define GPIO_DS_OUTPUT_ENABLE 0x0180
+#define GPIO_DS_OUTPUT_FRONTLR 0x0080
+#define GPIO_DS_OUTPUT_ENABLE 0x0100
#define LC_CONTROL_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000
@@ -56,6 +65,7 @@ struct xonar_wm87x6 {
struct snd_kcontrol *line_adcmux_control;
struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13];
+ struct snd_jack *hp_jack;
};
static void wm8776_write(struct oxygen *chip,
@@ -97,8 +107,12 @@ static void wm8766_write(struct oxygen *chip,
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
- if (reg < ARRAY_SIZE(data->wm8766_regs))
+ if (reg < ARRAY_SIZE(data->wm8766_regs)) {
+ if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+ (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
+ value &= ~WM8766_UPDATE;
data->wm8766_regs[reg] = value;
+ }
}
static void wm8766_write_cached(struct oxygen *chip,
@@ -107,12 +121,8 @@ static void wm8766_write_cached(struct oxygen *chip,
struct xonar_wm87x6 *data = chip->model_data;
if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
- value != data->wm8766_regs[reg]) {
- if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
- (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
- value &= ~WM8766_UPDATE;
+ value != data->wm8766_regs[reg])
wm8766_write(chip, reg, value);
- }
}
static void wm8776_registers_init(struct oxygen *chip)
@@ -141,7 +151,10 @@ static void wm8776_registers_init(struct oxygen *chip)
static void wm8766_registers_init(struct oxygen *chip)
{
+ struct xonar_wm87x6 *data = chip->model_data;
+
wm8766_write(chip, WM8766_RESET, 0);
+ wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]);
wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
wm8766_write(chip, WM8766_DAC_CTRL2,
WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
@@ -170,6 +183,40 @@ static void wm8776_init(struct oxygen *chip)
wm8776_registers_init(chip);
}
+static void wm8766_init(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ data->wm8766_regs[WM8766_DAC_CTRL] =
+ WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+ wm8766_registers_init(chip);
+}
+
+static void xonar_ds_handle_hp_jack(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+ bool hp_plugged;
+ unsigned int reg;
+
+ mutex_lock(&chip->mutex);
+
+ hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_DS_HP_DETECT);
+
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR,
+ GPIO_DS_OUTPUT_FRONTLR);
+
+ reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL;
+ if (hp_plugged)
+ reg |= WM8766_MUTEALL;
+ wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+
+ snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
+
+ mutex_unlock(&chip->mutex);
+}
+
static void xonar_ds_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@@ -178,16 +225,22 @@ static void xonar_ds_init(struct oxygen *chip)
data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
wm8776_init(chip);
- wm8766_registers_init(chip);
+ wm8766_init(chip);
- oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE,
- GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DS_HP_DETECT);
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
chip->interrupt_mask |= OXYGEN_INT_GPIO;
xonar_enable_output(chip);
+ snd_jack_new(chip->card, "Headphone",
+ SND_JACK_HEADPHONE, &data->hp_jack);
+ xonar_ds_handle_hp_jack(chip);
+
snd_component_add(chip->card, "WM8776");
snd_component_add(chip->card, "WM8766");
}
@@ -208,6 +261,7 @@ static void xonar_ds_resume(struct oxygen *chip)
wm8776_registers_init(chip);
wm8766_registers_init(chip);
xonar_enable_output(chip);
+ xonar_ds_handle_hp_jack(chip);
}
static void wm8776_adc_hardware_filter(unsigned int channel,
@@ -323,12 +377,27 @@ static void update_wm87x6_mute(struct oxygen *chip)
(chip->dac_mute ? WM8766_DMUTE_MASK : 0));
}
-static void xonar_ds_gpio_changed(struct oxygen *chip)
+static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
{
- u16 bits;
+ struct xonar_wm87x6 *data = chip->model_data;
+ unsigned int reg;
- bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT));
+ /*
+ * The WM8766 can mix left and right channels, but this setting
+ * applies to all three stereo pairs.
+ */
+ reg = data->wm8766_regs[WM8766_DAC_CTRL] &
+ ~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK);
+ if (mixed)
+ reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX;
+ else
+ reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+ wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+}
+
+static void xonar_ds_gpio_changed(struct oxygen *chip)
+{
+ xonar_ds_handle_hp_jack(chip);
}
static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
@@ -896,7 +965,10 @@ static const struct snd_kcontrol_new ds_controls[] = {
.put = wm8776_input_mux_put,
.private_value = 1 << 1,
},
- WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0),
+ WM8776_BIT_SWITCH("Front Mic Capture Switch",
+ WM8776_ADCMUX, 1 << 2, 0, 0),
+ WM8776_BIT_SWITCH("Aux Capture Switch",
+ WM8776_ADCMUX, 1 << 3, 0, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Filter Capture Enum",
@@ -1013,6 +1085,7 @@ static const struct oxygen_model model_xonar_ds = {
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm87x6_volume,
.update_dac_mute = update_wm87x6_mute,
+ .update_center_lfe_mix = update_wm8766_center_lfe_mix,
.gpio_changed = xonar_ds_gpio_changed,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index d19dc052c391..d5f5b440fc40 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1527,14 +1527,14 @@ snd_rme96_free(void *private_data)
static void
snd_rme96_free_spdif_pcm(struct snd_pcm *pcm)
{
- struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+ struct rme96 *rme96 = pcm->private_data;
rme96->spdif_pcm = NULL;
}
static void
snd_rme96_free_adat_pcm(struct snd_pcm *pcm)
{
- struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+ struct rme96 *rme96 = pcm->private_data;
rme96->adat_pcm = NULL;
}
@@ -1661,7 +1661,7 @@ static void
snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
int n;
- struct rme96 *rme96 = (struct rme96 *)entry->private_data;
+ struct rme96 *rme96 = entry->private_data;
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
@@ -2348,7 +2348,7 @@ snd_rme96_probe(struct pci_dev *pci,
if (err < 0)
return err;
card->private_free = snd_rme96_card_free;
- rme96 = (struct rme96 *)card->private_data;
+ rme96 = card->private_data;
rme96->card = card;
rme96->pci = pci;
snd_card_set_dev(card, &pci->dev);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index b92adef8e81e..599e09051663 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3284,7 +3284,7 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
static void
snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct hdsp *hdsp = (struct hdsp *) entry->private_data;
+ struct hdsp *hdsp = entry->private_data;
unsigned int status;
unsigned int status2;
char *pref_sync_ref;
@@ -4566,7 +4566,7 @@ static int hdsp_get_peak(struct hdsp *hdsp, struct hdsp_peak_rms __user *peak_rm
static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct hdsp *hdsp = (struct hdsp *)hw->private_data;
+ struct hdsp *hdsp = hw->private_data;
void __user *argp = (void __user *)arg;
int err;
@@ -5155,7 +5155,7 @@ static int snd_hdsp_free(struct hdsp *hdsp)
static void snd_hdsp_card_free(struct snd_card *card)
{
- struct hdsp *hdsp = (struct hdsp *) card->private_data;
+ struct hdsp *hdsp = card->private_data;
if (hdsp)
snd_hdsp_free(hdsp);
@@ -5181,7 +5181,7 @@ static int __devinit snd_hdsp_probe(struct pci_dev *pci,
if (err < 0)
return err;
- hdsp = (struct hdsp *) card->private_data;
+ hdsp = card->private_data;
card->private_free = snd_hdsp_card_free;
hdsp->dev = dev;
hdsp->pci = pci;
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 2f12da4da561..581a670e8261 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -579,7 +579,7 @@ static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
rate * delay_ms / 1000)
* substream->runtime->channels;
- pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
+ pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
__func__,
delay_ms,
rate,
diff --git a/sound/soc/s3c24xx/s3c-dma.c b/sound/soc/s3c24xx/s3c-dma.c
index 54bff83c98b4..243f79bf43bb 100644
--- a/sound/soc/s3c24xx/s3c-dma.c
+++ b/sound/soc/s3c24xx/s3c-dma.c
@@ -94,8 +94,7 @@ static void s3c_dma_enqueue(struct snd_pcm_substream *substream)
if ((pos + len) > prtd->dma_end) {
len = prtd->dma_end - pos;
- pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n",
- __func__, len);
+ pr_debug("%s: corrected dma len %ld\n", __func__, len);
}
ret = s3c2410_dma_enqueue(prtd->params->channel,
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 44d6d2ec964f..112984f4080f 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -65,6 +65,7 @@ config SND_USB_CAIAQ
* Native Instruments Guitar Rig Session I/O
* Native Instruments Guitar Rig mobile
* Native Instruments Traktor Kontrol X1
+ * Native Instruments Traktor Kontrol S4
To compile this driver as a module, choose M here: the module
will be called snd-usb-caiaq.
@@ -82,6 +83,7 @@ config SND_USB_CAIAQ_INPUT
* Native Instruments Kore Controller
* Native Instruments Kore Controller 2
* Native Instruments Audio Kontrol 1
+ * Native Instruments Traktor Kontrol S4
config SND_USB_US122L
tristate "Tascam US-122L USB driver"
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index 4328cad6c3a2..68b97477577b 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -111,7 +111,7 @@ static int stream_start(struct snd_usb_caiaqdev *dev)
memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
dev->input_panic = 0;
dev->output_panic = 0;
- dev->first_packet = 1;
+ dev->first_packet = 4;
dev->streaming = 1;
dev->warned = 0;
@@ -169,7 +169,7 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
}
static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
- struct snd_pcm_hw_params *hw_params)
+ struct snd_pcm_hw_params *hw_params)
{
debug("%s(%p)\n", __func__, sub);
return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
@@ -189,7 +189,7 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
#endif
static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+ 48000, 64000, 88200, 96000, 176400, 192000 };
static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
{
@@ -201,12 +201,39 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
debug("%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
- dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
+ int out_pos;
+
+ switch (dev->spec.data_alignment) {
+ case 0:
+ case 2:
+ out_pos = BYTES_PER_SAMPLE + 1;
+ break;
+ case 3:
+ default:
+ out_pos = 0;
+ break;
+ }
+
+ dev->period_out_count[index] = out_pos;
+ dev->audio_out_buf_pos[index] = out_pos;
} else {
- int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2;
- dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos;
- dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos;
+ int in_pos;
+
+ switch (dev->spec.data_alignment) {
+ case 0:
+ in_pos = BYTES_PER_SAMPLE + 2;
+ break;
+ case 2:
+ in_pos = BYTES_PER_SAMPLE;
+ break;
+ case 3:
+ default:
+ in_pos = 0;
+ break;
+ }
+
+ dev->period_in_count[index] = in_pos;
+ dev->audio_in_buf_pos[index] = in_pos;
}
if (dev->streaming)
@@ -221,7 +248,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
bytes_per_sample = BYTES_PER_SAMPLE;
- if (dev->spec.data_alignment == 2)
+ if (dev->spec.data_alignment >= 2)
bytes_per_sample++;
bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
@@ -253,6 +280,8 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd)
{
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
+ debug("%s(%p) cmd %d\n", __func__, sub, cmd);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -402,6 +431,61 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
}
}
+static void read_in_urb_mode3(struct snd_usb_caiaqdev *dev,
+ const struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+ int stream, i;
+
+ /* paranoia check */
+ if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM))
+ return;
+
+ for (i = 0; i < iso->actual_length;) {
+ for (stream = 0; stream < dev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = dev->sub_capture[stream];
+ char *audio_buf = NULL;
+ int c, n, sz = 0;
+
+ if (sub && !dev->input_panic) {
+ struct snd_pcm_runtime *rt = sub->runtime;
+ audio_buf = rt->dma_area;
+ sz = frames_to_bytes(rt, rt->buffer_size);
+ }
+
+ for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+ /* 3 audio data bytes, followed by 1 check byte */
+ if (audio_buf) {
+ for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+ audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
+
+ if (dev->audio_in_buf_pos[stream] == sz)
+ dev->audio_in_buf_pos[stream] = 0;
+ }
+
+ dev->period_in_count[stream] += BYTES_PER_SAMPLE;
+ }
+
+ i += BYTES_PER_SAMPLE;
+
+ if (usb_buf[i] != ((stream << 1) | c) &&
+ !dev->first_packet) {
+ if (!dev->input_panic)
+ printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
+ ((stream << 1) | c), usb_buf[i], c, stream, i);
+ dev->input_panic = 1;
+ }
+
+ i++;
+ }
+ }
+ }
+
+ if (dev->first_packet > 0)
+ dev->first_packet--;
+}
+
static void read_in_urb(struct snd_usb_caiaqdev *dev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
@@ -419,6 +503,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev,
case 2:
read_in_urb_mode2(dev, urb, iso);
break;
+ case 3:
+ read_in_urb_mode3(dev, urb, iso);
+ break;
}
if ((dev->input_panic || dev->output_panic) && !dev->warned) {
@@ -429,9 +516,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev,
}
}
-static void fill_out_urb(struct snd_usb_caiaqdev *dev,
- struct urb *urb,
- const struct usb_iso_packet_descriptor *iso)
+static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *dev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
struct snd_pcm_substream *sub;
@@ -457,9 +544,67 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev,
/* fill in the check bytes */
if (dev->spec.data_alignment == 2 &&
i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
- (dev->n_streams * CHANNELS_PER_STREAM))
- for (stream = 0; stream < dev->n_streams; stream++, i++)
- usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+ (dev->n_streams * CHANNELS_PER_STREAM))
+ for (stream = 0; stream < dev->n_streams; stream++, i++)
+ usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+ }
+}
+
+static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+ int stream, i;
+
+ for (i = 0; i < iso->length;) {
+ for (stream = 0; stream < dev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = dev->sub_playback[stream];
+ char *audio_buf = NULL;
+ int c, n, sz = 0;
+
+ if (sub) {
+ struct snd_pcm_runtime *rt = sub->runtime;
+ audio_buf = rt->dma_area;
+ sz = frames_to_bytes(rt, rt->buffer_size);
+ }
+
+ for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+ for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+ if (audio_buf) {
+ usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++];
+
+ if (dev->audio_out_buf_pos[stream] == sz)
+ dev->audio_out_buf_pos[stream] = 0;
+ } else {
+ usb_buf[i+n] = 0;
+ }
+ }
+
+ if (audio_buf)
+ dev->period_out_count[stream] += BYTES_PER_SAMPLE;
+
+ i += BYTES_PER_SAMPLE;
+
+ /* fill in the check byte pattern */
+ usb_buf[i++] = (stream << 1) | c;
+ }
+ }
+ }
+}
+
+static inline void fill_out_urb(struct snd_usb_caiaqdev *dev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ switch (dev->spec.data_alignment) {
+ case 0:
+ case 2:
+ fill_out_urb_mode_0(dev, urb, iso);
+ break;
+ case 3:
+ fill_out_urb_mode_3(dev, urb, iso);
+ break;
}
}
diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c
index 91c804cd2782..00e5d0a469e1 100644
--- a/sound/usb/caiaq/control.c
+++ b/sound/usb/caiaq/control.c
@@ -55,6 +55,10 @@ static int control_info(struct snd_kcontrol *kcontrol,
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
maxval = 127;
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ maxval = 31;
+ break;
}
if (is_intval) {
@@ -93,6 +97,7 @@ static int control_put(struct snd_kcontrol *kcontrol,
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
+ int v = ucontrol->value.integer.value[0];
unsigned char cmd = EP1_CMD_WRITE_IO;
if (dev->chip.usb_id ==
@@ -100,12 +105,27 @@ static int control_put(struct snd_kcontrol *kcontrol,
cmd = EP1_CMD_DIMM_LEDS;
if (pos & CNT_INTVAL) {
- dev->control_state[pos & ~CNT_INTVAL]
- = ucontrol->value.integer.value[0];
- snd_usb_caiaq_send_command(dev, cmd,
- dev->control_state, sizeof(dev->control_state));
+ int i = pos & ~CNT_INTVAL;
+
+ dev->control_state[i] = v;
+
+ if (dev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4)) {
+ int actual_len;
+
+ dev->ep8_out_buf[0] = i;
+ dev->ep8_out_buf[1] = v;
+
+ usb_bulk_msg(dev->chip.dev,
+ usb_sndbulkpipe(dev->chip.dev, 8),
+ dev->ep8_out_buf, sizeof(dev->ep8_out_buf),
+ &actual_len, 200);
+ } else {
+ snd_usb_caiaq_send_command(dev, cmd,
+ dev->control_state, sizeof(dev->control_state));
+ }
} else {
- if (ucontrol->value.integer.value[0])
+ if (v)
dev->control_state[pos / 8] |= 1 << (pos % 8);
else
dev->control_state[pos / 8] &= ~(1 << (pos % 8));
@@ -296,6 +316,179 @@ static struct caiaq_controller kontrolx1_controller[] = {
{ "LED Deck B: SYNC", 8 | CNT_INTVAL },
};
+static struct caiaq_controller kontrols4_controller[] = {
+ { "LED: Master: Quant", 10 | CNT_INTVAL },
+ { "LED: Master: Headphone", 11 | CNT_INTVAL },
+ { "LED: Master: Master", 12 | CNT_INTVAL },
+ { "LED: Master: Snap", 14 | CNT_INTVAL },
+ { "LED: Master: Warning", 15 | CNT_INTVAL },
+ { "LED: Master: Master button", 112 | CNT_INTVAL },
+ { "LED: Master: Snap button", 113 | CNT_INTVAL },
+ { "LED: Master: Rec", 118 | CNT_INTVAL },
+ { "LED: Master: Size", 119 | CNT_INTVAL },
+ { "LED: Master: Quant button", 120 | CNT_INTVAL },
+ { "LED: Master: Browser button", 121 | CNT_INTVAL },
+ { "LED: Master: Play button", 126 | CNT_INTVAL },
+ { "LED: Master: Undo button", 127 | CNT_INTVAL },
+
+ { "LED: Channel A: >", 4 | CNT_INTVAL },
+ { "LED: Channel A: <", 5 | CNT_INTVAL },
+ { "LED: Channel A: Meter 1", 97 | CNT_INTVAL },
+ { "LED: Channel A: Meter 2", 98 | CNT_INTVAL },
+ { "LED: Channel A: Meter 3", 99 | CNT_INTVAL },
+ { "LED: Channel A: Meter 4", 100 | CNT_INTVAL },
+ { "LED: Channel A: Meter 5", 101 | CNT_INTVAL },
+ { "LED: Channel A: Meter 6", 102 | CNT_INTVAL },
+ { "LED: Channel A: Meter clip", 103 | CNT_INTVAL },
+ { "LED: Channel A: Active", 114 | CNT_INTVAL },
+ { "LED: Channel A: Cue", 116 | CNT_INTVAL },
+ { "LED: Channel A: FX1", 149 | CNT_INTVAL },
+ { "LED: Channel A: FX2", 148 | CNT_INTVAL },
+
+ { "LED: Channel B: >", 2 | CNT_INTVAL },
+ { "LED: Channel B: <", 3 | CNT_INTVAL },
+ { "LED: Channel B: Meter 1", 89 | CNT_INTVAL },
+ { "LED: Channel B: Meter 2", 90 | CNT_INTVAL },
+ { "LED: Channel B: Meter 3", 91 | CNT_INTVAL },
+ { "LED: Channel B: Meter 4", 92 | CNT_INTVAL },
+ { "LED: Channel B: Meter 5", 93 | CNT_INTVAL },
+ { "LED: Channel B: Meter 6", 94 | CNT_INTVAL },
+ { "LED: Channel B: Meter clip", 95 | CNT_INTVAL },
+ { "LED: Channel B: Active", 122 | CNT_INTVAL },
+ { "LED: Channel B: Cue", 125 | CNT_INTVAL },
+ { "LED: Channel B: FX1", 147 | CNT_INTVAL },
+ { "LED: Channel B: FX2", 146 | CNT_INTVAL },
+
+ { "LED: Channel C: >", 6 | CNT_INTVAL },
+ { "LED: Channel C: <", 7 | CNT_INTVAL },
+ { "LED: Channel C: Meter 1", 105 | CNT_INTVAL },
+ { "LED: Channel C: Meter 2", 106 | CNT_INTVAL },
+ { "LED: Channel C: Meter 3", 107 | CNT_INTVAL },
+ { "LED: Channel C: Meter 4", 108 | CNT_INTVAL },
+ { "LED: Channel C: Meter 5", 109 | CNT_INTVAL },
+ { "LED: Channel C: Meter 6", 110 | CNT_INTVAL },
+ { "LED: Channel C: Meter clip", 111 | CNT_INTVAL },
+ { "LED: Channel C: Active", 115 | CNT_INTVAL },
+ { "LED: Channel C: Cue", 117 | CNT_INTVAL },
+ { "LED: Channel C: FX1", 151 | CNT_INTVAL },
+ { "LED: Channel C: FX2", 150 | CNT_INTVAL },
+
+ { "LED: Channel D: >", 0 | CNT_INTVAL },
+ { "LED: Channel D: <", 1 | CNT_INTVAL },
+ { "LED: Channel D: Meter 1", 81 | CNT_INTVAL },
+ { "LED: Channel D: Meter 2", 82 | CNT_INTVAL },
+ { "LED: Channel D: Meter 3", 83 | CNT_INTVAL },
+ { "LED: Channel D: Meter 4", 84 | CNT_INTVAL },
+ { "LED: Channel D: Meter 5", 85 | CNT_INTVAL },
+ { "LED: Channel D: Meter 6", 86 | CNT_INTVAL },
+ { "LED: Channel D: Meter clip", 87 | CNT_INTVAL },
+ { "LED: Channel D: Active", 123 | CNT_INTVAL },
+ { "LED: Channel D: Cue", 124 | CNT_INTVAL },
+ { "LED: Channel D: FX1", 145 | CNT_INTVAL },
+ { "LED: Channel D: FX2", 144 | CNT_INTVAL },
+
+ { "LED: Deck A: 1 (blue)", 22 | CNT_INTVAL },
+ { "LED: Deck A: 1 (green)", 23 | CNT_INTVAL },
+ { "LED: Deck A: 2 (blue)", 20 | CNT_INTVAL },
+ { "LED: Deck A: 2 (green)", 21 | CNT_INTVAL },
+ { "LED: Deck A: 3 (blue)", 18 | CNT_INTVAL },
+ { "LED: Deck A: 3 (green)", 19 | CNT_INTVAL },
+ { "LED: Deck A: 4 (blue)", 16 | CNT_INTVAL },
+ { "LED: Deck A: 4 (green)", 17 | CNT_INTVAL },
+ { "LED: Deck A: Load", 44 | CNT_INTVAL },
+ { "LED: Deck A: Deck C button", 45 | CNT_INTVAL },
+ { "LED: Deck A: In", 47 | CNT_INTVAL },
+ { "LED: Deck A: Out", 46 | CNT_INTVAL },
+ { "LED: Deck A: Shift", 24 | CNT_INTVAL },
+ { "LED: Deck A: Sync", 27 | CNT_INTVAL },
+ { "LED: Deck A: Cue", 26 | CNT_INTVAL },
+ { "LED: Deck A: Play", 25 | CNT_INTVAL },
+ { "LED: Deck A: Tempo up", 33 | CNT_INTVAL },
+ { "LED: Deck A: Tempo down", 32 | CNT_INTVAL },
+ { "LED: Deck A: Master", 34 | CNT_INTVAL },
+ { "LED: Deck A: Keylock", 35 | CNT_INTVAL },
+ { "LED: Deck A: Deck A", 37 | CNT_INTVAL },
+ { "LED: Deck A: Deck C", 36 | CNT_INTVAL },
+ { "LED: Deck A: Samples", 38 | CNT_INTVAL },
+ { "LED: Deck A: On Air", 39 | CNT_INTVAL },
+ { "LED: Deck A: Sample 1", 31 | CNT_INTVAL },
+ { "LED: Deck A: Sample 2", 30 | CNT_INTVAL },
+ { "LED: Deck A: Sample 3", 29 | CNT_INTVAL },
+ { "LED: Deck A: Sample 4", 28 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - A", 55 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - B", 54 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - C", 53 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - D", 52 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - E", 51 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - F", 50 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - G", 49 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - dot", 48 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - A", 63 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - B", 62 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - C", 61 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - D", 60 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - E", 59 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - F", 58 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - G", 57 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - dot", 56 | CNT_INTVAL },
+
+ { "LED: Deck B: 1 (blue)", 78 | CNT_INTVAL },
+ { "LED: Deck B: 1 (green)", 79 | CNT_INTVAL },
+ { "LED: Deck B: 2 (blue)", 76 | CNT_INTVAL },
+ { "LED: Deck B: 2 (green)", 77 | CNT_INTVAL },
+ { "LED: Deck B: 3 (blue)", 74 | CNT_INTVAL },
+ { "LED: Deck B: 3 (green)", 75 | CNT_INTVAL },
+ { "LED: Deck B: 4 (blue)", 72 | CNT_INTVAL },
+ { "LED: Deck B: 4 (green)", 73 | CNT_INTVAL },
+ { "LED: Deck B: Load", 180 | CNT_INTVAL },
+ { "LED: Deck B: Deck D button", 181 | CNT_INTVAL },
+ { "LED: Deck B: In", 183 | CNT_INTVAL },
+ { "LED: Deck B: Out", 182 | CNT_INTVAL },
+ { "LED: Deck B: Shift", 64 | CNT_INTVAL },
+ { "LED: Deck B: Sync", 67 | CNT_INTVAL },
+ { "LED: Deck B: Cue", 66 | CNT_INTVAL },
+ { "LED: Deck B: Play", 65 | CNT_INTVAL },
+ { "LED: Deck B: Tempo up", 185 | CNT_INTVAL },
+ { "LED: Deck B: Tempo down", 184 | CNT_INTVAL },
+ { "LED: Deck B: Master", 186 | CNT_INTVAL },
+ { "LED: Deck B: Keylock", 187 | CNT_INTVAL },
+ { "LED: Deck B: Deck B", 189 | CNT_INTVAL },
+ { "LED: Deck B: Deck D", 188 | CNT_INTVAL },
+ { "LED: Deck B: Samples", 190 | CNT_INTVAL },
+ { "LED: Deck B: On Air", 191 | CNT_INTVAL },
+ { "LED: Deck B: Sample 1", 71 | CNT_INTVAL },
+ { "LED: Deck B: Sample 2", 70 | CNT_INTVAL },
+ { "LED: Deck B: Sample 3", 69 | CNT_INTVAL },
+ { "LED: Deck B: Sample 4", 68 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - A", 175 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - B", 174 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - C", 173 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - D", 172 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - E", 171 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - F", 170 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - G", 169 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - dot", 168 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - A", 167 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - B", 166 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - C", 165 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - D", 164 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - E", 163 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - F", 162 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - G", 161 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - dot", 160 | CNT_INTVAL },
+
+ { "LED: FX1: dry/wet", 153 | CNT_INTVAL },
+ { "LED: FX1: 1", 154 | CNT_INTVAL },
+ { "LED: FX1: 2", 155 | CNT_INTVAL },
+ { "LED: FX1: 3", 156 | CNT_INTVAL },
+ { "LED: FX1: Mode", 157 | CNT_INTVAL },
+ { "LED: FX2: dry/wet", 129 | CNT_INTVAL },
+ { "LED: FX2: 1", 130 | CNT_INTVAL },
+ { "LED: FX2: 2", 131 | CNT_INTVAL },
+ { "LED: FX2: 3", 132 | CNT_INTVAL },
+ { "LED: FX2: Mode", 133 | CNT_INTVAL },
+};
+
static int __devinit add_controls(struct caiaq_controller *c, int num,
struct snd_usb_caiaqdev *dev)
{
@@ -354,6 +547,11 @@ int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
ret = add_controls(kontrolx1_controller,
ARRAY_SIZE(kontrolx1_controller), dev);
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ ret = add_controls(kontrols4_controller,
+ ARRAY_SIZE(kontrols4_controller), dev);
+ break;
}
return ret;
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index cdfb856bddd2..6480c3283c05 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -36,7 +36,7 @@
#include "input.h"
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.21");
+MODULE_DESCRIPTION("caiaq USB audio");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, RigKontrol3},"
@@ -48,7 +48,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, Audio 8 DJ},"
"{Native Instruments, Session I/O},"
"{Native Instruments, GuitarRig mobile}"
- "{Native Instruments, Traktor Kontrol X1}");
+ "{Native Instruments, Traktor Kontrol X1}"
+ "{Native Instruments, Traktor Kontrol S4}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -134,6 +135,11 @@ static struct usb_device_id snd_usb_id_table[] = {
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_TRAKTORKONTROLX1
},
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_TRAKTORKONTROLS4
+ },
{ /* terminator */ }
};
diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h
index f1117ecc84fd..e3d8a3efb35b 100644
--- a/sound/usb/caiaq/device.h
+++ b/sound/usb/caiaq/device.h
@@ -16,6 +16,7 @@
#define USB_PID_SESSIONIO 0x1915
#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define USB_PID_TRAKTORKONTROLX1 0x2305
+#define USB_PID_TRAKTORKONTROLS4 0xbaff
#define EP1_BUFSIZE 64
#define EP4_BUFSIZE 512
@@ -99,13 +100,14 @@ struct snd_usb_caiaqdev {
struct snd_pcm_substream *sub_capture[MAX_STREAMS];
/* Controls */
- unsigned char control_state[64];
+ unsigned char control_state[256];
+ unsigned char ep8_out_buf[2];
/* Linux input */
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
struct input_dev *input_dev;
char phys[64]; /* physical device path */
- unsigned short keycode[64];
+ unsigned short keycode[128];
struct urb *ep4_in_urb;
unsigned char ep4_in_buf[EP4_BUFSIZE];
#endif
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index dcb620796d9e..4432ef7a70a9 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -67,7 +67,12 @@ static unsigned short keycode_kore[] = {
KEY_BRL_DOT5
};
-#define KONTROLX1_INPUTS 40
+#define KONTROLX1_INPUTS (40)
+#define KONTROLS4_BUTTONS (12 * 8)
+#define KONTROLS4_AXIS (46)
+
+#define KONTROLS4_BUTTON(X) ((X) + BTN_MISC)
+#define KONTROLS4_ABS(X) ((X) + ABS_HAT0X)
#define DEG90 (range / 2)
#define DEG180 (range)
@@ -139,6 +144,13 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
#undef HIGH_PEAK
#undef LOW_PEAK
+static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *dev,
+ int axis, const unsigned char *buf,
+ int offset)
+{
+ input_report_abs(dev->input_dev, axis,
+ (buf[offset * 2] << 8) | buf[offset * 2 + 1]);
+}
static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
const unsigned char *buf,
@@ -148,36 +160,30 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
- input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]);
- input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(dev, ABS_X, buf, 2);
+ snd_caiaq_input_report_abs(dev, ABS_Y, buf, 0);
+ snd_caiaq_input_report_abs(dev, ABS_Z, buf, 1);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
- input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
- input_sync(input_dev);
- break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(dev, ABS_X, buf, 0);
+ snd_caiaq_input_report_abs(dev, ABS_Y, buf, 1);
+ snd_caiaq_input_report_abs(dev, ABS_Z, buf, 2);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
- input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8) | buf[9]);
- input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8) | buf[5]);
- input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]);
- input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_HAT2X, (buf[14] << 8) | buf[15]);
- input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]);
- input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8) | buf[7]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(dev, ABS_HAT0X, buf, 4);
+ snd_caiaq_input_report_abs(dev, ABS_HAT0Y, buf, 2);
+ snd_caiaq_input_report_abs(dev, ABS_HAT1X, buf, 6);
+ snd_caiaq_input_report_abs(dev, ABS_HAT1Y, buf, 1);
+ snd_caiaq_input_report_abs(dev, ABS_HAT2X, buf, 7);
+ snd_caiaq_input_report_abs(dev, ABS_HAT2Y, buf, 0);
+ snd_caiaq_input_report_abs(dev, ABS_HAT3X, buf, 5);
+ snd_caiaq_input_report_abs(dev, ABS_HAT3Y, buf, 3);
break;
}
+
+ input_sync(input_dev);
}
static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
@@ -250,6 +256,150 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
input_sync(input_dev);
}
+#define TKS4_MSGBLOCK_SIZE 16
+
+static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev,
+ const unsigned char *buf,
+ unsigned int len)
+{
+ while (len) {
+ unsigned int i, block_id = (buf[0] << 8) | buf[1];
+
+ switch (block_id) {
+ case 0:
+ /* buttons */
+ for (i = 0; i < KONTROLS4_BUTTONS; i++)
+ input_report_key(dev->input_dev, KONTROLS4_BUTTON(i),
+ (buf[4 + (i / 8)] >> (i % 8)) & 1);
+ break;
+
+ case 1:
+ /* left wheel */
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8));
+ /* right wheel */
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8));
+
+ /* rotary encoders */
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4);
+ input_report_abs(dev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf);
+
+ break;
+ case 2:
+ /* Volume Fader Channel D */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(0), buf, 1);
+ /* Volume Fader Channel B */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(1), buf, 2);
+ /* Volume Fader Channel A */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(2), buf, 3);
+ /* Volume Fader Channel C */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(3), buf, 4);
+ /* Loop Volume */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(4), buf, 6);
+ /* Crossfader */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(7), buf, 7);
+
+ break;
+
+ case 3:
+ /* Tempo Fader R */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(6), buf, 3);
+ /* Tempo Fader L */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(5), buf, 4);
+ /* Mic Volume */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(8), buf, 6);
+ /* Cue Mix */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(9), buf, 7);
+
+ break;
+
+ case 4:
+ /* Wheel distance sensor L */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(10), buf, 1);
+ /* Wheel distance sensor R */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(11), buf, 2);
+ /* Channel D EQ - Filter */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(12), buf, 3);
+ /* Channel D EQ - Low */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(13), buf, 4);
+ /* Channel D EQ - Mid */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(14), buf, 5);
+ /* Channel D EQ - Hi */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(15), buf, 6);
+ /* FX2 - dry/wet */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(16), buf, 7);
+
+ break;
+
+ case 5:
+ /* FX2 - 1 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(17), buf, 1);
+ /* FX2 - 2 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(18), buf, 2);
+ /* FX2 - 3 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(19), buf, 3);
+ /* Channel B EQ - Filter */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(20), buf, 4);
+ /* Channel B EQ - Low */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(21), buf, 5);
+ /* Channel B EQ - Mid */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(22), buf, 6);
+ /* Channel B EQ - Hi */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(23), buf, 7);
+
+ break;
+
+ case 6:
+ /* Channel A EQ - Filter */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(24), buf, 1);
+ /* Channel A EQ - Low */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(25), buf, 2);
+ /* Channel A EQ - Mid */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(26), buf, 3);
+ /* Channel A EQ - Hi */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(27), buf, 4);
+ /* Channel C EQ - Filter */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(28), buf, 5);
+ /* Channel C EQ - Low */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(29), buf, 6);
+ /* Channel C EQ - Mid */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(30), buf, 7);
+
+ break;
+
+ case 7:
+ /* Channel C EQ - Hi */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(31), buf, 1);
+ /* FX1 - wet/dry */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(32), buf, 2);
+ /* FX1 - 1 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(33), buf, 3);
+ /* FX1 - 2 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(34), buf, 4);
+ /* FX1 - 3 */
+ snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(35), buf, 5);
+
+ break;
+
+ default:
+ debug("%s(): bogus block (id %d)\n",
+ __func__, block_id);
+ return;
+ }
+
+ len -= TKS4_MSGBLOCK_SIZE;
+ buf += TKS4_MSGBLOCK_SIZE;
+ }
+
+ input_sync(dev->input_dev);
+}
+
static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
{
struct snd_usb_caiaqdev *dev = urb->context;
@@ -259,11 +409,11 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
if (urb->status || !dev || urb != dev->ep4_in_urb)
return;
- if (urb->actual_length < 24)
- goto requeue;
-
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+ if (urb->actual_length < 24)
+ goto requeue;
+
if (buf[0] & 0x3)
snd_caiaq_input_read_io(dev, buf + 1, 7);
@@ -271,6 +421,10 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
snd_caiaq_input_read_analog(dev, buf + 8, 16);
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length);
+ break;
}
requeue:
@@ -289,6 +443,7 @@ static int snd_usb_caiaq_input_open(struct input_dev *idev)
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
return -EIO;
break;
@@ -306,6 +461,7 @@ static void snd_usb_caiaq_input_close(struct input_dev *idev)
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
usb_kill_urb(dev->ep4_in_urb);
break;
}
@@ -456,6 +612,46 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLS4_BUTTONS);
+ for (i = 0; i < KONTROLS4_BUTTONS; i++)
+ dev->keycode[i] = KONTROLS4_BUTTON(i);
+ input->keycodemax = KONTROLS4_BUTTONS;
+
+ for (i = 0; i < KONTROLS4_AXIS; i++) {
+ int axis = KONTROLS4_ABS(i);
+ input->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+ }
+
+ /* 36 analog potentiometers and faders */
+ for (i = 0; i < 36; i++)
+ input_set_abs_params(input, KONTROLS4_ABS(i), 0, 0xfff, 0, 10);
+
+ /* 2 encoder wheels */
+ input_set_abs_params(input, KONTROLS4_ABS(36), 0, 0x3ff, 0, 1);
+ input_set_abs_params(input, KONTROLS4_ABS(37), 0, 0x3ff, 0, 1);
+
+ /* 9 rotary encoders */
+ for (i = 0; i < 9; i++)
+ input_set_abs_params(input, KONTROLS4_ABS(38+i), 0, 0xf, 0, 1);
+
+ dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->ep4_in_urb) {
+ ret = -ENOMEM;
+ goto exit_free_idev;
+ }
+
+ usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+ usb_rcvbulkpipe(usb_dev, 0x4),
+ dev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, dev);
+
+ snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+
+ break;
+
default:
/* no input methods supported on this device */
goto exit_free_idev;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4eabafa5b037..800f7cb4f251 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -300,9 +300,13 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
*rchip = NULL;
- if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
- snd_usb_get_speed(dev) != USB_SPEED_FULL &&
- snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
+ switch (snd_usb_get_speed(dev)) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ break;
+ default:
snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
return -ENXIO;
}
@@ -378,11 +382,22 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
if (len < sizeof(card->longname))
usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
- strlcat(card->longname,
- snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
- snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
- ", high speed",
- sizeof(card->longname));
+ switch (snd_usb_get_speed(dev)) {
+ case USB_SPEED_LOW:
+ strlcat(card->longname, ", low speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_FULL:
+ strlcat(card->longname, ", full speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_HIGH:
+ strlcat(card->longname, ", high speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_SUPER:
+ strlcat(card->longname, ", super speed", sizeof(card->longname));
+ break;
+ default:
+ break;
+ }
snd_usb_audio_create_proc(chip);
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index ef0a07e34844..b0ef9f501896 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -405,8 +405,6 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
break;
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
- case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
- case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
/* doesn't set the sample rate attribute, but supports it */
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
break;
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index d48d6f8f6ac9..f280c1903c25 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -103,11 +103,16 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *alts)
{
- if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
- get_endpoint(alts, 0)->bInterval >= 1 &&
- get_endpoint(alts, 0)->bInterval <= 4)
- return get_endpoint(alts, 0)->bInterval - 1;
- else
- return 0;
+ switch (snd_usb_get_speed(chip->dev)) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ if (get_endpoint(alts, 0)->bInterval >= 1 &&
+ get_endpoint(alts, 0)->bInterval <= 4)
+ return get_endpoint(alts, 0)->bInterval - 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
}
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index b9c2bc65f51a..156cd0716c42 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -834,7 +834,14 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
if (!ep->ports[0].active)
return;
- count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
+ switch (snd_usb_get_speed(ep->umidi->dev)) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ count = 1;
+ break;
+ default:
+ count = 2;
+ }
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 3b5135c93062..f49756c1b837 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -466,7 +466,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
return 0;
}
/* check whether the period time is >= the data packet interval */
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+ if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) {
ptime = 125 * (1 << fp->datainterval);
if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max);
@@ -734,7 +734,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
}
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
- if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
/* full speed devices have fixed data packet interval */
ptmin = 1000;
if (ptmin == 1000)
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index f5e3f356b95f..3c650ab3c91d 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -107,7 +107,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
snd_iprintf(buffer, "\n");
}
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+ if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
snd_iprintf(buffer, " Data packet interval: %d us\n",
125 * (1 << fp->datainterval));
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 2e8003f98fca..c86c613e0b96 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1136,11 +1136,34 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ /* has ID 0x0066 when not in "Advanced Driver" mode */
+ USB_DEVICE(0x0582, 0x0064),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "EDIROL", */
+ /* .product_name = "PCR-1", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
/* has ID 0x0067 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0065),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "PCR-1",
+ /* .vendor_name = "EDIROL", */
+ /* .product_name = "PCR-1", */
.ifnum = 0,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const struct snd_usb_midi_endpoint_info) {
@@ -1525,6 +1548,50 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /* has ID 0x0110 when not in Advanced Driver mode */
+ USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "A-PRO", */
+ .ifnum = 1,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0003,
+ .in_cables = 0x0007
+ }
+ }
+},
+{
+ USB_DEVICE(0x0582, 0x0113),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "BOSS", */
+ /* .product_name = "ME-25", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
@@ -1830,7 +1897,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
USB_DEVICE(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
- /* .product_name = "Fast Track Ultra 8", */
+ /* .product_name = "Fast Track Ultra", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = & (const struct snd_usb_audio_quirk[]) {
@@ -1840,11 +1907,51 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
/* interface 3 (MIDI) is standard compliant */
{
@@ -1867,11 +1974,51 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
/* interface 3 (MIDI) is standard compliant */
{
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
index de607d4411ac..8deeaad10f10 100644
--- a/sound/usb/urb.c
+++ b/sound/usb/urb.c
@@ -244,7 +244,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
else
subs->curpacksize = maxsize;
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+ if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
packs_per_ms = 8 >> subs->datainterval;
else
packs_per_ms = 1;