diff options
author | Takashi Iwai <tiwai@suse.de> | 2010-10-25 09:56:32 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-10-25 09:56:32 +0200 |
commit | 79fc84c7e0d2fe89c4e82f3a26fd8b0d13c31703 (patch) | |
tree | e32a4c267dc1b5950012e18c1e71d8057140f1f5 /sound | |
parent | f6f94e2ab1b33f0082ac22d71f66385a60d8157f (diff) | |
parent | 97c44b2dbd0060e2e6bd56236eb638ab02ec7f30 (diff) |
Merge branch 'topic/misc' into for-linus
Diffstat (limited to 'sound')
76 files changed, 3535 insertions, 1107 deletions
diff --git a/sound/core/init.c b/sound/core/init.c index ec4a50ce565..2de45fbd70f 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -607,11 +607,16 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, return -EEXIST; } for (idx = 0; idx < snd_ecards_limit; idx++) { - if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) - goto __exist; + if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) { + if (card == snd_cards[idx]) + goto __ok; + else + goto __exist; + } } strcpy(card->id, buf1); snd_info_card_id_change(card); +__ok: mutex_unlock(&snd_card_mutex); return count; diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index f50ebf20df9..822dd56993c 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) { @@ -618,8 +618,10 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) + if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { + up_read(&card->controls_rwsem); return; + } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) @@ -658,7 +660,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, return; down_read(&card->controls_rwsem); if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { - up_read(&fmixer->card->controls_rwsem); + up_read(&card->controls_rwsem); return; } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -691,7 +693,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 +742,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 +755,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 +768,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 +778,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; @@ -797,7 +799,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -813,7 +815,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)) @@ -826,6 +828,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -847,7 +850,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -861,7 +864,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)) @@ -880,6 +883,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -925,7 +929,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 ac242a377ae..6b4b1287b31 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 e23e0e7ab26..a1707cca9c6 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 d4eb2ef8078..8bc7cb3db33 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -142,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, #ifdef RULES_DEBUG #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -char *snd_pcm_hw_param_names[] = { +static const char * const snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), @@ -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 480c38623da..c8961165277 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 d4a07f9ff2c..1a8440c8b13 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 00000000000..12b44b0b677 --- /dev/null +++ b/sound/drivers/aloop.c @@ -0,0 +1,1258 @@ +/* + * 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/info.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; + unsigned int pause; +}; + +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); + } + if (dpcm->period_size_frac <= dpcm->irq_pos) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + 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); + dpcm->timer.expires = 0; +} + +#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, *cruntime; + 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; + cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> + substream->runtime; + check = runtime->format != cruntime->format || + runtime->rate != cruntime->rate || + runtime->channels != cruntime->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, stream = 1 << substream->stream; + + 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; + spin_lock(&cable->lock); + cable->running |= stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&cable->lock); + cable->running &= ~stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_stop(dpcm); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock(&cable->lock); + cable->pause |= stream; + spin_unlock(&cable->lock); + loopback_timer_stop(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock(&cable->lock); + dpcm->last_jiffies = jiffies; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + break; + default: + return -EINVAL; + } + return 0; +} + +static void params_change_substream(struct loopback_pcm *dpcm, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_runtime *dst_runtime; + + if (dpcm == NULL || dpcm->substream == NULL) + return; + dst_runtime = dpcm->substream->runtime; + if (dst_runtime == NULL) + return; + dst_runtime->hw = dpcm->cable->hw; +} + +static void params_change(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; + + 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; + params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], + runtime); + params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], + runtime); +} + +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; + 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)) || + (get_setup(dpcm)->notify && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + params_change(substream); + 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 = 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); + capt->silent_size = 0; + } +} + +#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 unsigned int 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; + unsigned int running; + + spin_lock(&cable->lock); + running = cable->running ^ cable->pause; + if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { + delta_play = jiffies - dpcm_play->last_jiffies; + dpcm_play->last_jiffies += delta_play; + } + + if (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 running; + } + + 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 running; + } + /* 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); + return running; +} + +static void loopback_timer_function(unsigned long data) +{ + struct loopback_pcm *dpcm = (struct loopback_pcm *)data; + unsigned int running; + + running = loopback_pos_update(dpcm->cable); + if (running & (1 << dpcm->substream->stream)) { + loopback_timer_start(dpcm); + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + 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 | SNDRV_PCM_INFO_PAUSE), + .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, + /* note check overflow in frac_pos() using pcm_rate_shift before + changing period_bytes_max value */ + .period_bytes_max = 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 rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + + struct snd_pcm_hardware *hw = rule->private; + struct snd_mask *maskp = hw_param_mask(params, rule->var); + + maskp->bits[0] &= (u_int32_t)hw->formats; + maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); + memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ + if (! maskp->bits[0] && ! maskp->bits[1]) + return -EINVAL; + return 0; +} + +static int rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hardware *hw = rule->private; + struct snd_interval t; + + t.min = hw->rate_min; + t.max = hw->rate_max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hardware *hw = rule->private; + struct snd_interval t; + + t.min = hw->channels_min; + t.max = hw->channels_max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +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); + + /* use dynamic rules based on actual runtime->hw values */ + /* note that the default rules created in the PCM midlevel code */ + /* are cached -> they do not reflect the actual state */ + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + rule_format, &runtime->hw, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + rule_rate, &runtime->hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + rule_channels, &runtime->hw, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto unlock; + + runtime->private_data = dpcm; + runtime->private_free = loopback_runtime_free; + if (get_notify(dpcm)) + 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 ^ 1]; + 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].channels; + 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; +} + +#ifdef CONFIG_PROC_FS + +static void print_dpcm_info(struct snd_info_buffer *buffer, + struct loopback_pcm *dpcm, + const char *id) +{ + snd_iprintf(buffer, " %s\n", id); + if (dpcm == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); + snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); + snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); + snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); + snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); + snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); + snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); + snd_iprintf(buffer, " update_pending:\t%u\n", + dpcm->period_update_pending); + snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); + snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); + snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", + dpcm->last_jiffies, jiffies); + snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); +} + +static void print_substream_info(struct snd_info_buffer *buffer, + struct loopback *loopback, + int sub, + int num) +{ + struct loopback_cable *cable = loopback->cables[sub][num]; + + snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); + if (cable == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " valid: %u\n", cable->valid); + snd_iprintf(buffer, " running: %u\n", cable->running); + snd_iprintf(buffer, " pause: %u\n", cable->pause); + print_dpcm_info(buffer, cable->streams[0], "Playback"); + print_dpcm_info(buffer, cable->streams[1], "Capture"); +} + +static void print_cable_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct loopback *loopback = entry->private_data; + int sub, num; + + mutex_lock(&loopback->cable_lock); + num = entry->name[strlen(entry->name)-1]; + num = num == '0' ? 0 : 1; + for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) + print_substream_info(buffer, loopback, sub, num); + mutex_unlock(&loopback->cable_lock); +} + +static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) +{ + char name[32]; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "cable#%d", cidx); + err = snd_card_proc_new(loopback->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, loopback, print_cable_info); + return 0; +} + +#else /* !CONFIG_PROC_FS */ + +#define loopback_proc_new(loopback, cidx) do { } while (0) + +#endif + +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; + loopback_proc_new(loopback, 0); + loopback_proc_new(loopback, 1); + 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 0e631c3221e..f4cd49336f3 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 42d7844ecd0..57ccba88700 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 c6990c68079..52064cfa91f 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 c73d30c4f46..8d781e419e2 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 bbcbf92a8eb..3cb75bc9769 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 f7aa637b0d1..aac8dc15c2f 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 00000000000..e307066d431 --- /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 00000000000..9a97643cb71 --- /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 00000000000..189441141df --- /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 00000000000..ee54df082b9 --- /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 f26eac8d811..3e4a58b7291 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 81284a8fa0c..2259e3f726a 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 6fe27b9d944..00000000000 --- 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/Kconfig b/sound/oss/Kconfig index a513651fa14..76c09021807 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -545,11 +545,3 @@ config SOUND_KAHLUA endif # SOUND_OSS -config SOUND_SH_DAC_AUDIO - tristate "SuperH DAC audio support" - depends on CPU_SH3 && HIGH_RES_TIMERS - -config SOUND_SH_DAC_AUDIO_CHANNEL - int "DAC channel" - default "1" - depends on SOUND_SH_DAC_AUDIO diff --git a/sound/oss/Makefile b/sound/oss/Makefile index 567b8a74178..96f14dcd0cd 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_SOUND_OSS) += sound.o # Please leave it as is, cause the link order is significant ! -obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c index c6f2621221b..a8f626d99c5 100644 --- a/sound/oss/au1550_ac97.c +++ b/sound/oss/au1550_ac97.c @@ -43,7 +43,6 @@ #include <linux/sound.h> #include <linux/slab.h> #include <linux/soundcard.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -77,6 +76,7 @@ /* Boot options * 0 = no VRA, 1 = use VRA if codec supports it */ +static DEFINE_MUTEX(au1550_ac97_mutex); static int vra = 1; module_param(vra, bool, 0); MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); @@ -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; @@ -798,9 +798,9 @@ au1550_llseek(struct file *file, loff_t offset, int origin) static int au1550_open_mixdev(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); file->private_data = &au1550_state; - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return 0; } @@ -820,13 +820,13 @@ 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; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); ret = mixdev_ioctl(codec, cmd, arg); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return 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,12 +1250,12 @@ 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; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); mutex_lock(&s->sem); if (vma->vm_flags & VM_WRITE) db = &s->dma_dac; @@ -1283,7 +1283,7 @@ au1550_mmap(struct file *file, struct vm_area_struct *vma) db->mapped = 1; out: mutex_unlock(&s->sem); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } @@ -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; @@ -1781,9 +1781,9 @@ au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); ret = au1550_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } @@ -1804,7 +1804,7 @@ au1550_open(struct inode *inode, struct file *file) #endif file->private_data = s; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); /* wait for device to become free */ mutex_lock(&s->open_mutex); while (s->open_mode & file->f_mode) { @@ -1861,21 +1861,21 @@ au1550_open(struct inode *inode, struct file *file) out: mutex_unlock(&s->open_mutex); out2: - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } 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(); + mutex_lock(&au1550_ac97_mutex); if (file->f_mode & FMODE_WRITE) { - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); drain_dac(s, file->f_flags & O_NONBLOCK); - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); } mutex_lock(&s->open_mutex); @@ -1892,7 +1892,7 @@ au1550_release(struct inode *inode, struct file *file) s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); mutex_unlock(&s->open_mutex); wake_up(&s->open_wait); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return 0; } diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c index 6ecd41abb06..87e2c72651f 100644 --- a/sound/oss/dmasound/dmasound_core.c +++ b/sound/oss/dmasound/dmasound_core.c @@ -181,7 +181,7 @@ #include <linux/init.h> #include <linux/soundcard.h> #include <linux/poll.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <asm/uaccess.h> @@ -194,6 +194,7 @@ * Declarations */ +static DEFINE_MUTEX(dmasound_core_mutex); int dmasound_catchRadius = 0; module_param(dmasound_catchRadius, int, 0); @@ -323,22 +324,22 @@ static struct { static int mixer_open(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (!try_module_get(dmasound.mach.owner)) { - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return -ENODEV; } mixer.busy = 1; - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } static int mixer_release(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); mixer.busy = 0; module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } @@ -370,9 +371,9 @@ static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = mixer_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } @@ -752,9 +753,9 @@ static int sq_open(struct inode *inode, struct file *file) { int rc; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (!try_module_get(dmasound.mach.owner)) { - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return -ENODEV; } @@ -799,11 +800,11 @@ static int sq_open(struct inode *inode, struct file *file) sound_set_format(AFMT_MU_LAW); } #endif - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; out: module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return rc; } @@ -869,7 +870,7 @@ static int sq_release(struct inode *inode, struct file *file) { int rc = 0; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (file->f_mode & FMODE_WRITE) { if (write_sq.busy) @@ -900,7 +901,7 @@ static int sq_release(struct inode *inode, struct file *file) write_sq_wake_up(file); /* checks f_mode */ #endif /* blocking open() */ - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return rc; } @@ -1141,9 +1142,9 @@ static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = sq_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } @@ -1257,7 +1258,7 @@ static int state_open(struct inode *inode, struct file *file) int len = 0; int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = -EBUSY; if (state.busy) goto out; @@ -1329,16 +1330,16 @@ printk("dmasound: stat buffer used %d bytes\n", len) ; state.len = len; ret = 0; out: - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } static int state_release(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); state.busy = 0; module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c index 2e48b17667d..b4c1eb504c2 100644 --- a/sound/oss/msnd_pinnacle.c +++ b/sound/oss/msnd_pinnacle.c @@ -39,7 +39,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/gfp.h> #include <asm/irq.h> #include <asm/io.h> @@ -79,6 +79,7 @@ dev.rec_sample_rate / \ dev.rec_channels) +static DEFINE_MUTEX(msnd_pinnacle_mutex); static multisound_dev_t dev; #ifndef HAVE_DSPCODEH @@ -651,12 +652,12 @@ static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -EINVAL; - lock_kernel(); + mutex_lock(&msnd_pinnacle_mutex); if (minor == dev.dsp_minor) ret = dsp_ioctl(file, cmd, arg); else if (minor == dev.mixer_minor) ret = mixer_ioctl(cmd, arg); - unlock_kernel(); + mutex_unlock(&msnd_pinnacle_mutex); return ret; } @@ -761,7 +762,7 @@ static int dev_open(struct inode *inode, struct file *file) int minor = iminor(inode); int err = 0; - lock_kernel(); + mutex_lock(&msnd_pinnacle_mutex); if (minor == dev.dsp_minor) { if ((file->f_mode & FMODE_WRITE && test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) || @@ -791,7 +792,7 @@ static int dev_open(struct inode *inode, struct file *file) } else err = -EINVAL; out: - unlock_kernel(); + mutex_unlock(&msnd_pinnacle_mutex); return err; } @@ -800,14 +801,14 @@ static int dev_release(struct inode *inode, struct file *file) int minor = iminor(inode); int err = 0; - lock_kernel(); + mutex_lock(&msnd_pinnacle_mutex); if (minor == dev.dsp_minor) err = dsp_release(file); else if (minor == dev.mixer_minor) { /* nothing */ } else err = -EINVAL; - unlock_kernel(); + mutex_unlock(&msnd_pinnacle_mutex); return err; } diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c deleted file mode 100644 index 479e3025a8a..00000000000 --- a/sound/oss/sh_dac_audio.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * sound/oss/sh_dac_audio.c - * - * SH DAC based sound :( - * - * Copyright (C) 2004,2005 Andriy Skulysh - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/linkage.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/sound.h> -#include <linux/smp_lock.h> -#include <linux/soundcard.h> -#include <linux/interrupt.h> -#include <linux/hrtimer.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/irq.h> -#include <asm/delay.h> -#include <asm/clock.h> -#include <cpu/dac.h> -#include <asm/machvec.h> -#include <mach/hp6xx.h> -#include <asm/hd64461.h> - -#define MODNAME "sh_dac_audio" - -#define BUFFER_SIZE 48000 - -static int rate; -static int empty; -static char *data_buffer, *buffer_begin, *buffer_end; -static int in_use, device_major; -static struct hrtimer hrtimer; -static ktime_t wakeups_per_second; - -static void dac_audio_start_timer(void) -{ - hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); -} - -static void dac_audio_stop_timer(void) -{ - hrtimer_cancel(&hrtimer); -} - -static void dac_audio_reset(void) -{ - dac_audio_stop_timer(); - buffer_begin = buffer_end = data_buffer; - empty = 1; -} - -static void dac_audio_sync(void) -{ - while (!empty) - schedule(); -} - -static void dac_audio_start(void) -{ - if (mach_is_hp6xx()) { - u16 v = __raw_readw(HD64461_GPADR); - v &= ~HD64461_GPADR_SPEAKER; - __raw_writew(v, HD64461_GPADR); - } - - sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); -} -static void dac_audio_stop(void) -{ - dac_audio_stop_timer(); - - if (mach_is_hp6xx()) { - u16 v = __raw_readw(HD64461_GPADR); - v |= HD64461_GPADR_SPEAKER; - __raw_writew(v, HD64461_GPADR); - } - - sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); - sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); -} - -static void dac_audio_set_rate(void) -{ - wakeups_per_second = ktime_set(0, 1000000000 / rate); -} - -static int dac_audio_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int val; - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - dac_audio_sync(); - return 0; - - case SNDCTL_DSP_RESET: - dac_audio_reset(); - return 0; - - case SNDCTL_DSP_GETFMTS: - return put_user(AFMT_U8, (int *)arg); - - case SNDCTL_DSP_SETFMT: - return put_user(AFMT_U8, (int *)arg); - - case SNDCTL_DSP_NONBLOCK: - spin_lock(&file->f_lock); - file->f_flags |= O_NONBLOCK; - spin_unlock(&file->f_lock); - return 0; - - case SNDCTL_DSP_GETCAPS: - return 0; - - case SOUND_PCM_WRITE_RATE: - val = *(int *)arg; - if (val > 0) { - rate = val; - dac_audio_set_rate(); - } - return put_user(rate, (int *)arg); - - case SNDCTL_DSP_STEREO: - return put_user(0, (int *)arg); - - case SOUND_PCM_WRITE_CHANNELS: - return put_user(1, (int *)arg); - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_PROFILE: - return -EINVAL; - - case SNDCTL_DSP_GETBLKSIZE: - return put_user(BUFFER_SIZE, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - return 0; - - default: - printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n", - cmd); - return -EINVAL; - } - return -EINVAL; -} - -static long dac_audio_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) -{ - int ret; - - lock_kernel(); - ret = dac_audio_ioctl(file, cmd, arg); - unlock_kernel(); - - return ret; -} - -static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count, - loff_t * ppos) -{ - int free; - int nbytes; - - if (!count) { - dac_audio_sync(); - return 0; - } - - free = buffer_begin - buffer_end; - - if (free < 0) - free += BUFFER_SIZE; - if ((free == 0) && (empty)) - free = BUFFER_SIZE; - if (count > free) - count = free; - if (buffer_begin > buffer_end) { - if (copy_from_user((void *)buffer_end, buf, count)) - return -EFAULT; - - buffer_end += count; - } else { - nbytes = data_buffer + BUFFER_SIZE - buffer_end; - if (nbytes > count) { - if (copy_from_user((void *)buffer_end, buf, count)) - return -EFAULT; - buffer_end += count; - } else { - if (copy_from_user((void *)buffer_end, buf, nbytes)) - return -EFAULT; - if (copy_from_user - ((void *)data_buffer, buf + nbytes, count - nbytes)) - return -EFAULT; - buffer_end = data_buffer + count - nbytes; - } - } - - if (empty) { - empty = 0; - dac_audio_start_timer(); - } - - return count; -} - -static ssize_t dac_audio_read(struct file *file, char *buf, size_t count, - loff_t * ppos) -{ - return -EINVAL; -} - -static int dac_audio_open(struct inode *inode, struct file *file) -{ - if (file->f_mode & FMODE_READ) - return -ENODEV; - - lock_kernel(); - if (in_use) { - unlock_kernel(); - return -EBUSY; - } - - in_use = 1; - - dac_audio_start(); - unlock_kernel(); - return 0; -} - -static int dac_audio_release(struct inode *inode, struct file *file) -{ - dac_audio_sync(); - dac_audio_stop(); - in_use = 0; - - return 0; -} - -const struct file_operations dac_audio_fops = { - .read = dac_audio_read, - .write = dac_audio_write, - .unlocked_ioctl = dac_audio_unlocked_ioctl, - .open = dac_audio_open, - .release = dac_audio_release, -}; - -static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle) -{ - if (!empty) { - sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); - buffer_begin++; - - if (buffer_begin == data_buffer + BUFFER_SIZE) - buffer_begin = data_buffer; - if (buffer_begin == buffer_end) - empty = 1; - } - - if (!empty) - hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); - - return HRTIMER_NORESTART; -} - -static int __init dac_audio_init(void) -{ - if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) { - printk(KERN_ERR "Cannot register dsp device"); - return device_major; - } - - in_use = 0; - - data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); - if (data_buffer == NULL) - return -ENOMEM; - - dac_audio_reset(); - rate = 8000; - dac_audio_set_rate(); - - /* Today: High Resolution Timer driven DAC playback. - * The timer callback gets called once per sample. Ouch. - * - * Future: A much better approach would be to use the - * SH7720 CMT+DMAC+DAC hardware combination like this: - * - Program sample rate using CMT0 or CMT1 - * - Program DMAC to use CMT for timing and output to DAC - * - Play sound using DMAC, let CPU sleep. - * - While at it, rewrite this driver to use ALSA. - */ - - hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer.function = sh_dac_audio_timer; - - return 0; -} - -static void __exit dac_audio_exit(void) -{ - unregister_sound_dsp(device_major); - kfree((void *)data_buffer); -} - -module_init(dac_audio_init); -module_exit(dac_audio_exit); - -MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); -MODULE_DESCRIPTION("SH DAC sound driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index 07f803e6d20..46c0d03dbec 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -40,7 +40,7 @@ #include <linux/major.h> #include <linux/delay.h> #include <linux/proc_fs.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/device.h> @@ -56,6 +56,7 @@ * Table for permanently allocated memory (used when unloading the module) */ void * sound_mem_blocks[MAX_MEM_BLOCKS]; +static DEFINE_MUTEX(soundcard_mutex); int sound_nblocks = 0; /* Persistent DMA buffers */ @@ -151,7 +152,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof * big one anyway, we might as well bandage here.. */ - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { @@ -169,7 +170,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof case SND_DEV_MIDIN: ret = MIDIbuf_read(dev, file, buf, count); } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -178,7 +179,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou int dev = iminor(file->f_path.dentry->d_inode); int ret = -EINVAL; - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { case SND_DEV_SEQ: @@ -196,7 +197,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou ret = MIDIbuf_write(dev, file, buf, count); break; } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -210,7 +211,7 @@ static int sound_open(struct inode *inode, struct file *file) printk(KERN_ERR "Invalid minor device %d\n", dev); return -ENXIO; } - lock_kernel(); + mutex_lock(&soundcard_mutex); switch (dev & 0x0f) { case SND_DEV_CTL: dev >>= 4; @@ -247,15 +248,15 @@ static int sound_open(struct inode *inode, struct file *file) retval = -ENXIO; } - unlock_kernel(); - return 0; + mutex_unlock(&soundcard_mutex); + return retval; } static int sound_release(struct inode *inode, struct file *file) { int dev = iminor(inode); - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_release(dev=%d)\n", dev)); switch (dev & 0x0f) { case SND_DEV_CTL: @@ -280,7 +281,7 @@ static int sound_release(struct inode *inode, struct file *file) default: printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return 0; } @@ -354,7 +355,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cmd == OSS_GETVERSION) return __put_user(SOUND_VERSION, (int __user *)p); - lock_kernel(); + mutex_lock(&soundcard_mutex); if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ (dev & 0x0f) != SND_DEV_CTL) { dtype = dev & 0x0f; @@ -369,7 +370,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = sound_mixer_ioctl(dev >> 4, cmd, p); break; } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -399,7 +400,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -439,35 +440,35 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma) printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); return -EINVAL; } - lock_kernel(); + mutex_lock(&soundcard_mutex); if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ dmap = audio_devs[dev]->dmap_out; else if (vma->vm_flags & VM_READ) dmap = audio_devs[dev]->dmap_in; else { printk(KERN_ERR "Sound: Undefined mmap() access\n"); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EINVAL; } if (dmap == NULL) { printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EIO; } if (dmap->raw_buf == NULL) { printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EIO; } if (dmap->mapping_flags) { printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EIO; } if (vma->vm_pgoff != 0) { printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EINVAL; } size = vma->vm_end - vma->vm_start; @@ -478,7 +479,7 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma) if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) { - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return -EAGAIN; } @@ -490,7 +491,7 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma) memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return 0; } diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c index b15840ad252..44357d877a2 100644 --- a/sound/oss/swarm_cs4297a.c +++ b/sound/oss/swarm_cs4297a.c @@ -68,7 +68,6 @@ #include <linux/delay.h> #include <linux/sound.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/soundcard.h> #include <linux/ac97_codec.h> #include <linux/pci.h> @@ -94,6 +93,7 @@ struct cs4297a_state; +static DEFINE_MUTEX(swarm_cs4297a_mutex); static void stop_dac(struct cs4297a_state *s); static void stop_adc(struct cs4297a_state *s); static void start_dac(struct cs4297a_state *s); @@ -1535,7 +1535,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n")); - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); list_for_each(entry, &cs4297a_devs) { s = list_entry(entry, struct cs4297a_state, list); @@ -1547,7 +1547,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n")); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return -ENODEV; } VALIDATE_STATE(s); @@ -1555,7 +1555,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n")); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return nonseekable_open(inode, file); } @@ -1575,10 +1575,10 @@ static int cs4297a_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd, arg); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } @@ -2350,9 +2350,9 @@ static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = cs4297a_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } @@ -2509,9 +2509,9 @@ static int cs4297a_open(struct inode *inode, struct file *file) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = cs4297a_open(inode, file); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c index 8cd73cdd88a..643f1113b1d 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -145,7 +145,6 @@ #include <linux/init.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <linux/wait.h> #include <linux/interrupt.h> #include <linux/mutex.h> @@ -160,6 +159,7 @@ #ifdef VWSND_DEBUG +static DEFINE_MUTEX(vwsnd_mutex); static int shut_up = 1; /* @@ -2891,11 +2891,11 @@ static long vwsnd_audio_ioctl(struct file *file, vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; int ret; - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->io_mutex); ret = vwsnd_audio_do_ioctl(file, cmd, arg); mutex_unlock(&devc->io_mutex); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return ret; } @@ -2922,7 +2922,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) DBGE("(inode=0x%p, file=0x%p)\n", inode, file); - lock_kernel(); + mutex_lock(&vwsnd_mutex); INC_USE_COUNT; for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) @@ -2930,7 +2930,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) if (devc == NULL) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ENODEV; } @@ -2939,13 +2939,13 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) mutex_unlock(&devc->open_mutex); if (file->f_flags & O_NONBLOCK) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -EBUSY; } interruptible_sleep_on(&devc->open_wait); if (signal_pending(current)) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ERESTARTSYS; } mutex_lock(&devc->open_mutex); @@ -2998,7 +2998,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) file->private_data = devc; DBGRV(); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return 0; } @@ -3012,7 +3012,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file) vwsnd_port_t *wport = NULL, *rport = NULL; int err = 0; - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->io_mutex); { DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); @@ -3040,7 +3040,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file) wake_up(&devc->open_wait); DEC_USE_COUNT; DBGR(); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return err; } @@ -3068,18 +3068,18 @@ static int vwsnd_mixer_open(struct inode *inode, struct file *file) DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); INC_USE_COUNT; - lock_kernel(); + mutex_lock(&vwsnd_mutex); for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) if (devc->mixer_minor == iminor(inode)) break; if (devc == NULL) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ENODEV; } file->private_data = devc; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return 0; } @@ -3223,7 +3223,7 @@ static long vwsnd_mixer_ioctl(struct file *file, DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->mix_mutex); { if ((cmd & ~nrmask) == MIXER_READ(0)) @@ -3234,7 +3234,7 @@ static long vwsnd_mixer_ioctl(struct file *file, retval = -EINVAL; } mutex_unlock(&devc->mix_mutex); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return retval; } diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index e7a8cd058ef..12e34653b8a 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/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index c92f493d341..557c782ae4f 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -23,7 +23,7 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex) if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); - // Intialize AC97 codec stuff. + // Initialize AC97 codec stuff. ac97.private_data = vortex; ac97.scaps = AC97_SCAP_NO_SPDIF; err = snd_ac97_mixer(pbus, &ac97, &vortex->codec); diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index 14b8d9a91aa..f19c1107725 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -670,8 +670,9 @@ struct snd_ca0106_details { gpio_type = 2 -> shared side-out/line-in. */ int i2c_adc; /* with i2c_adc=1, the driver adds some capture volume controls, phone, mic, line-in and aux. */ - int spi_dac; /* spi_dac=1 adds the mute switch for each analog - output, front, rear, etc. */ + u16 spi_dac; /* spi_dac = 0 -> no spi interface for DACs + spi_dac = 0x<front><rear><center-lfe><side> + -> specifies DAC id for each channel pair. */ }; // definition of the chip-specific record diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 0a3d3d6e77b..d2d12c08f93 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -227,7 +227,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "Audigy SE [SB0570]", .gpio_type = 1, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , /* New Audigy LS. Has a different DAC. */ /* SB0570: * CTRL:CA0106-DAT @@ -238,7 +238,17 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "Audigy SE OEM [SB0570a]", .gpio_type = 1, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , + /* Sound Blaster 5.1vx + * Tested: Playback on front, rear, center/lfe speakers + * Not-Tested: Capture + */ + { .serial = 0x10041102, + .name = "Sound Blaster 5.1vx [SB1070]", + .gpio_type = 1, + .i2c_adc = 0, + .spi_dac = 0x0124 + } , /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ /* SB0438 * CTRL:CA0106-DAT @@ -254,7 +264,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "MSI K8N Diamond MB", .gpio_type = 2, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , /* Giga-byte GA-G1975X mobo * Novell bnc#395807 */ @@ -483,16 +493,18 @@ static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime) } static const int spi_dacd_reg[] = { - [PCM_FRONT_CHANNEL] = SPI_DACD4_REG, - [PCM_REAR_CHANNEL] = SPI_DACD0_REG, - [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG, - [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_REG, + SPI_DACD0_REG, + SPI_DACD1_REG, + SPI_DACD2_REG, + 0, + SPI_DACD4_REG, }; static const int spi_dacd_bit[] = { - [PCM_FRONT_CHANNEL] = SPI_DACD4_BIT, - [PCM_REAR_CHANNEL] = SPI_DACD0_BIT, - [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT, - [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT, + SPI_DACD0_BIT, + SPI_DACD1_BIT, + SPI_DACD2_BIT, + 0, + SPI_DACD4_BIT, }; static void restore_spdif_bits(struct snd_ca0106 *chip, int idx) @@ -504,6 +516,45 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx) } } +static int snd_ca0106_channel_dac(struct snd_ca0106_details *details, + int channel_id) +{ + switch (channel_id) { + case PCM_FRONT_CHANNEL: + return (details->spi_dac & 0xf000) >> (4 * 3); + case PCM_REAR_CHANNEL: + return (details->spi_dac & 0x0f00) >> (4 * 2); + case PCM_CENTER_LFE_CHANNEL: + return (details->spi_dac & 0x00f0) >> (4 * 1); + case PCM_UNKNOWN_CHANNEL: + return (details->spi_dac & 0x000f) >> (4 * 0); + default: + snd_printk(KERN_DEBUG "ca0106: unknown channel_id %d\n", + channel_id); + } + return 0; +} + +static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id, + int power) +{ + if (chip->details->spi_dac) { + const int dac = snd_ca0106_channel_dac(chip->details, + channel_id); + const int reg = spi_dacd_reg[dac]; + const int bit = spi_dacd_bit[dac]; + + if (power) + /* Power up */ + chip->spi_dac_reg[reg] &= ~bit; + else + /* Power down */ + chip->spi_dac_reg[reg] |= bit; + return snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + } + return 0; +} + /* open_playback callback */ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id) @@ -543,12 +594,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr return err; snd_pcm_set_sync(substream); - if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) { - const int reg = spi_dacd_reg[channel_id]; - - /* Power up dac */ - chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id]; - err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + /* Front channel dac should already be on */ + if (channel_id != PCM_FRONT_CHANNEL) { + err = snd_ca0106_pcm_power_dac(chip, channel_id, 1); if (err < 0) return err; } @@ -568,13 +616,14 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream) restore_spdif_bits(chip, epcm->channel_id); - if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) { - const int reg = spi_dacd_reg[epcm->channel_id]; - - /* Power down DAC */ - chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id]; - snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + /* Front channel dac should stay on */ + if (epcm->channel_id != PCM_FRONT_CHANNEL) { + int err; + err = snd_ca0106_pcm_power_dac(chip, epcm->channel_id, 0); + if (err < 0) + return err; } + /* FIXME: maybe zero others */ return 0; } @@ -1002,29 +1051,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 */ @@ -1362,7 +1409,7 @@ static unsigned int spi_dac_init[] = { SPI_REG(12, 0x00), SPI_REG(SPI_LDA4_REG, SPI_DA_BIT_0dB), SPI_REG(SPI_RDA4_REG, SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE), - SPI_REG(SPI_DACD4_REG, 0x00), + SPI_REG(SPI_DACD4_REG, SPI_DACD4_BIT), }; static unsigned int i2c_adc_init[][2] = { @@ -1541,7 +1588,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) /* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */ } - if (chip->details->spi_dac == 1) { + if (chip->details->spi_dac) { /* The SB0570 use SPI to control DAC. */ int size, n; @@ -1553,6 +1600,9 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) if (reg < ARRAY_SIZE(chip->spi_dac_reg)) chip->spi_dac_reg[reg] = spi_dac_init[n]; } + + /* Enable front dac only */ + snd_ca0106_pcm_power_dac(chip, PCM_FRONT_CHANNEL, 1); } } diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 85fd315d999..630aa499818 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -676,28 +676,65 @@ static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = I2C_VOLUME("Aux Capture Volume", 3), }; -#define SPI_SWITCH(xname,reg,bit) \ -{ \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = spi_mute_info, \ - .get = spi_mute_get, \ - .put = spi_mute_put, \ - .private_value = (reg<<SPI_REG_SHIFT) | (bit) \ -} - -static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[] -__devinitdata = { - SPI_SWITCH("Analog Front Playback Switch", - SPI_DMUTE4_REG, SPI_DMUTE4_BIT), - SPI_SWITCH("Analog Rear Playback Switch", - SPI_DMUTE0_REG, SPI_DMUTE0_BIT), - SPI_SWITCH("Analog Center/LFE Playback Switch", - SPI_DMUTE2_REG, SPI_DMUTE2_BIT), - SPI_SWITCH("Analog Side Playback Switch", - SPI_DMUTE1_REG, SPI_DMUTE1_BIT), +static const int spi_dmute_reg[] = { + SPI_DMUTE0_REG, + SPI_DMUTE1_REG, + SPI_DMUTE2_REG, + 0, + SPI_DMUTE4_REG, +}; +static const int spi_dmute_bit[] = { + SPI_DMUTE0_BIT, + SPI_DMUTE1_BIT, + SPI_DMUTE2_BIT, + 0, + SPI_DMUTE4_BIT, }; +static struct snd_kcontrol_new __devinit +snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details, + int channel_id) +{ + struct snd_kcontrol_new spi_switch = {0}; + int reg, bit; + int dac_id; + + spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + spi_switch.info = spi_mute_info; + spi_switch.get = spi_mute_get; + spi_switch.put = spi_mute_put; + + switch (channel_id) { + case PCM_FRONT_CHANNEL: + spi_switch.name = "Analog Front Playback Switch"; + dac_id = (details->spi_dac & 0xf000) >> (4 * 3); + break; + case PCM_REAR_CHANNEL: + spi_switch.name = "Analog Rear Playback Switch"; + dac_id = (details->spi_dac & 0x0f00) >> (4 * 2); + break; + case PCM_CENTER_LFE_CHANNEL: + spi_switch.name = "Analog Center/LFE Playback Switch"; + dac_id = (details->spi_dac & 0x00f0) >> (4 * 1); + break; + case PCM_UNKNOWN_CHANNEL: + spi_switch.name = "Analog Side Playback Switch"; + dac_id = (details->spi_dac & 0x000f) >> (4 * 0); + break; + default: + /* Unused channel */ + spi_switch.name = NULL; + dac_id = 0; + } + reg = spi_dmute_reg[dac_id]; + bit = spi_dmute_bit[dac_id]; + + spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit; + + return spi_switch; +} + static int __devinit remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; @@ -832,8 +869,18 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) if (err < 0) return err; } - if (emu->details->spi_dac == 1) - ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); + if (emu->details->spi_dac) { + int i; + for (i = 0;; i++) { + struct snd_kcontrol_new ctl; + ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i); + if (!ctl.name) + break; + err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu)); + if (err < 0) + return err; + } + } /* Create virtual master controls */ vmaster = snd_ctl_make_virtual_master("Master Playback Volume", @@ -845,7 +892,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) return err; add_slaves(card, vmaster, slave_vols); - if (emu->details->spi_dac == 1) { + if (emu->details->spi_dac) { vmaster = snd_ctl_make_virtual_master("Master Playback Switch", NULL); if (!vmaster) diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 8578c70c61f..bab564824ef 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/ice1712/delta.c b/sound/pci/ice1712/delta.c index d216362626d..712c1710f9a 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 f7f14df81f2..1a0ac6cd650 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 6bc3f91b728..cdb873f5da5 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 2a8e5cd8f2d..e36ddb94c38 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/oxygen.c b/sound/pci/oxygen/oxygen.c index 6c0a11adb2a..98a8eb3c92f 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -79,6 +79,7 @@ static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = { { OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF }, + { OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN }, @@ -505,7 +506,8 @@ static const struct oxygen_model model_generic = { PLAYBACK_2_TO_AC97_1 | CAPTURE_0_FROM_I2S_1 | CAPTURE_1_FROM_SPDIF | - CAPTURE_2_FROM_AC97_1, + CAPTURE_2_FROM_AC97_1 | + AC97_CD_INPUT, .dac_channels = 8, .dac_volume_min = 0, .dac_volume_max = 255, diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index a3409edcfb5..7d5222caa0a 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -34,6 +34,7 @@ /* CAPTURE_3_FROM_I2S_3 not implemented */ #define MIDI_OUTPUT 0x0800 #define MIDI_INPUT 0x1000 +#define AC97_CD_INPUT 0x2000 enum { CONTROL_SPDIF_PCM, diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 7e93cf88443..e5ebe56fb0c 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -308,25 +308,46 @@ static void oxygen_restore_eeprom(struct oxygen *chip, } } -static void pci_bridge_magic(void) +static void configure_pcie_bridge(struct pci_dev *pci) { - struct pci_dev *pci = NULL; + enum { PEX811X, PI7C9X110 }; + static const struct pci_device_id bridge_ids[] = { + { PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X }, + { PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X }, + { PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 }, + { } + }; + struct pci_dev *bridge; + const struct pci_device_id *id; u32 tmp; - for (;;) { - /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */ - pci = pci_get_device(0x12d8, 0xe110, pci); - if (!pci) - break; - /* - * ... configure its secondary internal arbiter to park to - * the secondary port, instead of to the last master. - */ - if (!pci_read_config_dword(pci, 0x40, &tmp)) { - tmp |= 1; - pci_write_config_dword(pci, 0x40, tmp); - } - /* Why? Try asking C-Media. */ + if (!pci->bus || !pci->bus->self) + return; + bridge = pci->bus->self; + + id = pci_match_id(bridge_ids, bridge); + if (!id) + return; + + switch (id->driver_data) { + case PEX811X: /* PLX PEX8111/PEX8112 PCIe/PCI bridge */ + pci_read_config_dword(bridge, 0x48, &tmp); + tmp |= 1; /* enable blind prefetching */ + tmp |= 1 << 11; /* enable beacon generation */ + pci_write_config_dword(bridge, 0x48, tmp); + + pci_write_config_dword(bridge, 0x84, 0x0c); + pci_read_config_dword(bridge, 0x88, &tmp); + tmp &= ~(7 << 27); + tmp |= 2 << 27; /* set prefetch size to 128 bytes */ + pci_write_config_dword(bridge, 0x88, tmp); + break; + + case PI7C9X110: /* Pericom PI7C9X110 PCIe/PCI bridge */ + pci_read_config_dword(bridge, 0x40, &tmp); + tmp |= 1; /* park the PCI arbiter to the sound chip */ + pci_write_config_dword(bridge, 0x40, tmp); + break; } } @@ -613,7 +634,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, snd_card_set_dev(card, &pci->dev); card->private_free = oxygen_card_free; - pci_bridge_magic(); + configure_pcie_bridge(pci); oxygen_init(chip); chip->model.init(chip); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index f375b8a2786..2849b36f5f7 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -708,7 +708,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | ((stereo) << 16) | (index), \ } -static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); +static DECLARE_TLV_DB_SCALE(monitor_db_scale, -600, 600, 0); static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); @@ -972,6 +972,9 @@ static int add_controls(struct oxygen *chip, if (!strcmp(template.name, "Stereo Upmixing") && chip->model.dac_channels == 2) continue; + if (!strncmp(template.name, "CD Capture ", 11) && + !(chip->model.device_config & AC97_CD_INPUT)) + continue; if (!strcmp(template.name, "Master Playback Volume") && chip->model.dac_tlv) { template.tlv.p = chip->model.dac_tlv; diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 9dff6954c39..814667442eb 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -56,8 +56,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = { .channels_max = 2, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; static const struct snd_pcm_hardware oxygen_multichannel_hardware = { @@ -82,8 +82,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = { .channels_max = 8, .buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX_MULTICH, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN, }; static const struct snd_pcm_hardware oxygen_ac97_hardware = { @@ -100,8 +100,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = { .channels_max = 2, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 72de159d456..4dcd41b7825 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -436,13 +436,15 @@ /* OXYGEN_CHANNEL_* */ #define OXYGEN_CODEC_VERSION 0xe4 -#define OXYGEN_XCID_MASK 0x07 +#define OXYGEN_CODEC_ID_MASK 0x07 #define OXYGEN_REVISION 0xe6 -#define OXYGEN_REVISION_XPKGID_MASK 0x0007 +#define OXYGEN_PACKAGE_ID_MASK 0x0007 +#define OXYGEN_PACKAGE_ID_8786 0x0004 +#define OXYGEN_PACKAGE_ID_8787 0x0006 +#define OXYGEN_PACKAGE_ID_8788 0x0007 #define OXYGEN_REVISION_MASK 0xfff8 -#define OXYGEN_REVISION_2 0x0008 /* bit flag */ -#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */ +#define OXYGEN_REVISION_2 0x0008 #define OXYGEN_OFFSIN_48K 0xe8 #define OXYGEN_OFFSBASE_48K 0xe9 diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 06c863e86e3..469010a8b84 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -25,9 +25,9 @@ #include "xonar.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_DESCRIPTION("Asus AVx00 driver"); +MODULE_DESCRIPTION("Asus Virtuoso driver"); MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -49,6 +49,7 @@ static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = { { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835e) }, { OXYGEN_PCI_SUBID(0x1043, 0x838e) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 7c4986b27f2..aa27c31049a 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -367,13 +367,6 @@ static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip, static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); -static int xonar_d1_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - static int xonar_d1_mixer_init(struct oxygen *chip) { int err; @@ -391,7 +384,6 @@ static const struct oxygen_model model_xonar_d1 = { .longname = "Asus Virtuoso 100", .chip = "AV200", .init = xonar_d1_init, - .control_filter = xonar_d1_control_filter, .mixer_init = xonar_d1_mixer_init, .cleanup = xonar_d1_cleanup, .suspend = xonar_d1_suspend, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index ba18fb546b4..d491fd6c0be 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -132,6 +132,18 @@ * GPIO 5 <- 0 */ +/* + * Xonar HDAV1.3 Slim + * ------------------ + * + * CMI8788: + * + * GPIO 1 -> enable output + * + * TXD -> HDMI controller + * RXD <- HDMI controller + */ + #include <linux/pci.h> #include <linux/delay.h> #include <linux/mutex.h> @@ -362,7 +374,6 @@ static void xonar_st_init_common(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; - data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; data->hp_gain_offset = 2*-18; @@ -408,6 +419,7 @@ static void xonar_st_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + data->generic.anti_pop_delay = 100; data->has_cs2000 = 1; data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; @@ -428,6 +440,7 @@ static void xonar_stx_init(struct oxygen *chip) struct xonar_pcm179x *data = chip->model_data; xonar_st_init_i2c(chip); + data->generic.anti_pop_delay = 800; data->generic.ext_power_reg = OXYGEN_GPI_DATA; data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->generic.ext_power_bit = GPI_EXT_POWER; @@ -915,13 +928,6 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template) return 0; } -static int xonar_st_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - static int add_pcm1796_controls(struct oxygen *chip) { int err; @@ -991,7 +997,8 @@ static const struct oxygen_model model_xonar_d2 = { CAPTURE_0_FROM_I2S_2 | CAPTURE_1_FROM_SPDIF | MIDI_OUTPUT | - MIDI_INPUT, + MIDI_INPUT | + AC97_CD_INPUT, .dac_channels = 8, .dac_volume_min = 255 - 2*60, .dac_volume_max = 255, @@ -1037,7 +1044,6 @@ static const struct oxygen_model model_xonar_st = { .longname = "Asus Virtuoso 100", .chip = "AV200", .init = xonar_st_init, - .control_filter = xonar_st_control_filter, .mixer_init = xonar_st_mixer_init, .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, @@ -1108,6 +1114,9 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model.resume = xonar_stx_resume; chip->model.set_dac_params = set_pcm1796_params; break; + case 0x835e: + snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n"); + return -ENODEV; default: return -EINVAL; } diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index b82c1cfa96f..200f7601276 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", @@ -956,13 +1028,6 @@ static const struct snd_kcontrol_new lc_controls[] = { LC_CONTROL_ALC, wm8776_ngth_db_scale), }; -static int xonar_ds_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - static int xonar_ds_mixer_init(struct oxygen *chip) { struct xonar_wm87x6 *data = chip->model_data; @@ -999,10 +1064,9 @@ static int xonar_ds_mixer_init(struct oxygen *chip) static const struct oxygen_model model_xonar_ds = { .shortname = "Xonar DS", - .longname = "Asus Virtuoso 200", + .longname = "Asus Virtuoso 66", .chip = "AV200", .init = xonar_ds_init, - .control_filter = xonar_ds_control_filter, .mixer_init = xonar_ds_mixer_init, .cleanup = xonar_ds_cleanup, .suspend = xonar_ds_suspend, @@ -1013,6 +1077,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 d19dc052c39..d5f5b440fc4 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 d6fa7bfd9aa..0b720cf7783 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; @@ -5156,7 +5156,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); @@ -5182,7 +5182,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/tumbler.c b/sound/ppc/tumbler.c index 20afdf9772e..961d9829769 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -785,7 +785,7 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix) if (! mix->i2c.client) return -ENODEV; if (mix->capture_source) - mix->acs = mix->acs |= 2; + mix->acs |= 2; else mix->acs &= ~2; return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs); diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 40eccfe9e35..4948a79f86a 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c @@ -150,7 +150,7 @@ static int __init sffsdr_init(void) sffsdr_snd_resources, ARRAY_SIZE(sffsdr_snd_resources)); if (ret) { - printk(KERN_ERR "platform device add ressources failed\n"); + printk(KERN_ERR "platform device add resources failed\n"); goto error; } diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index 209c25994c7..4719558289d 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -182,7 +182,7 @@ static int neo1973_gta02_voice_hw_params( if (ret < 0) return ret; - /* configue and enable PLL for 12.288MHz output */ + /* configure and enable PLL for 12.288MHz output */ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, iis_clkrate / 4, 12288000); if (ret < 0) diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 0cb4f86f6d1..4ac620988e7 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -201,7 +201,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* configue and enable PLL for 12.288MHz output */ + /* configure and enable PLL for 12.288MHz output */ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, iis_clkrate / 4, 12288000); if (ret < 0) diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index ff0b2a8fd25..5ae1eae9f6d 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -128,6 +128,9 @@ snd_emux_init_hwdep(struct snd_emux *emu) strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME); hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE; hw->ops.ioctl = snd_emux_hwdep_ioctl; + /* The ioctl parameter types are compatible between 32- and + * 64-bit architectures, so use the same function. */ + hw->ops.ioctl_compat = snd_emux_hwdep_ioctl; hw->exclusive = 1; hw->private_data = emu; if ((err = snd_card_register(emu->card)) < 0) diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 44d6d2ec964..112984f4080 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 4328cad6c3a..68b97477577 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 91c804cd278..00e5d0a469e 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 cdfb856bddd..6480c3283c0 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 f1117ecc84f..e3d8a3efb35 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 dcb620796d9..4432ef7a70a 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 4eabafa5b03..800f7cb4f25 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 ef0a07e3484..b0ef9f50189 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 d48d6f8f6ac..f280c1903c2 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 b9c2bc65f51..25bce7e5b1a 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -784,7 +784,7 @@ static struct usb_protocol_ops snd_usbmidi_novation_ops = { }; /* - * "raw" protocol: used by the MOTU FastLane. + * "raw" protocol: just move raw MIDI bytes from/to the endpoint */ static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep, @@ -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); @@ -2115,7 +2122,7 @@ int snd_usbmidi_create(struct snd_card *card, umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; - case QUIRK_MIDI_FASTLANE: + case QUIRK_MIDI_RAW_BYTES: umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; /* * Interface 1 contains isochronous endpoints, but with the same @@ -2126,7 +2133,8 @@ int snd_usbmidi_create(struct snd_card *card, * interface 0, so we have to make sure that the USB core looks * again at interface 0 by calling usb_set_interface() on it. */ - usb_set_interface(umidi->dev, 0, 0); + if (umidi->usb_id == USB_ID(0x07fd, 0x0001)) /* MOTU Fastlane */ + usb_set_interface(umidi->dev, 0, 0); err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; case QUIRK_MIDI_EMAGIC: diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3ed3901369c..f2d74d654b3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -759,8 +759,6 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl) */ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) { - struct snd_usb_audio *chip = cval->mixer->chip; - /* for failsafe */ cval->min = default_min; cval->max = cval->min + 1; @@ -783,7 +781,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->id, snd_usb_ctrl_intf(chip), cval->control, cval->id); + cval->id, snd_usb_ctrl_intf(cval->mixer->chip), cval->control, cval->id); return -EINVAL; } if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { @@ -1642,9 +1640,10 @@ static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = cval->max; - if ((int)uinfo->value.enumerated.item >= cval->max) + if (uinfo->value.enumerated.item >= cval->max) uinfo->value.enumerated.item = cval->max - 1; - strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]); + strlcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); return 0; } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e7df1e5e3f2..7dae05d8783 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -60,6 +60,7 @@ static const struct rc_config { { USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */ { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ + { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi */ { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ }; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3b5135c9306..f49756c1b83 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 f5e3f356b95..3c650ab3c91 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 2e8003f98fc..ad7079d1676 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -240,9 +240,21 @@ YAMAHA_DEVICE(0x104f, NULL), YAMAHA_DEVICE(0x1050, NULL), YAMAHA_DEVICE(0x1051, NULL), YAMAHA_DEVICE(0x1052, NULL), +YAMAHA_INTERFACE(0x1053, 0, NULL), +YAMAHA_INTERFACE(0x1054, 0, NULL), +YAMAHA_DEVICE(0x1055, NULL), +YAMAHA_DEVICE(0x1056, NULL), +YAMAHA_DEVICE(0x1057, NULL), +YAMAHA_DEVICE(0x1058, NULL), +YAMAHA_DEVICE(0x1059, NULL), +YAMAHA_DEVICE(0x105a, NULL), +YAMAHA_DEVICE(0x105b, NULL), +YAMAHA_DEVICE(0x105c, NULL), +YAMAHA_DEVICE(0x105d, NULL), YAMAHA_DEVICE(0x2000, "DGP-7"), YAMAHA_DEVICE(0x2001, "DGP-5"), YAMAHA_DEVICE(0x2002, NULL), +YAMAHA_DEVICE(0x2003, NULL), YAMAHA_DEVICE(0x5000, "CS1D"), YAMAHA_DEVICE(0x5001, "DSP1D"), YAMAHA_DEVICE(0x5002, "DME32"), @@ -1136,11 +1148,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 +1560,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 +1909,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 +1919,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 +1986,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 */ { @@ -1919,7 +2078,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = & (const struct snd_usb_audio_quirk[]) { { .ifnum = 0, - .type = QUIRK_MIDI_FASTLANE + .type = QUIRK_MIDI_RAW_BYTES }, { .ifnum = 1, @@ -2068,6 +2227,15 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + USB_DEVICE(0x1235, 0x000e), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Novation", */ + /* .product_name = "Launchpad", */ + .ifnum = 0, + .type = QUIRK_MIDI_RAW_BYTES + } +}, +{ USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { .vendor_name = "Novation", diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 9a9da09586a..cf8bf088394 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -287,7 +287,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk, [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk, [QUIRK_MIDI_NOVATION] = create_any_midi_quirk, - [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk, + [QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk, [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk, [QUIRK_MIDI_CME] = create_any_midi_quirk, [QUIRK_MIDI_AKAI] = create_any_midi_quirk, diff --git a/sound/usb/urb.c b/sound/usb/urb.c index de607d4411a..8deeaad10f1 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; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 24d3319cc34..db3eb21627e 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -70,7 +70,7 @@ enum quirk_type { QUIRK_MIDI_YAMAHA, QUIRK_MIDI_MIDIMAN, QUIRK_MIDI_NOVATION, - QUIRK_MIDI_FASTLANE, + QUIRK_MIDI_RAW_BYTES, QUIRK_MIDI_EMAGIC, QUIRK_MIDI_CME, QUIRK_MIDI_AKAI, diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index 2a528e56afd..287ef73b123 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -36,9 +36,9 @@ plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the cost of easier triggered i.e. aeolus xruns (128 or 256frames, 2periods works but is useless cause of crackling). - + This is a first "proof of concept" implementation. - Later, funcionalities should migrate to more apropriate places: + Later, functionalities should migrate to more apropriate places: Userland: - The jackd could mmap its float-pcm buffers directly from alsa-lib. - alsa-lib could provide power of 2 period sized shaping combined with int/float @@ -54,7 +54,7 @@ #include <linux/gfp.h> #include "usbusx2yaudio.c" -#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1) +#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1 #include <sound/hwdep.h> |