diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-11-01 18:58:18 +0100 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-11-01 18:58:18 +0100 |
commit | 6e1bd1ab1d9ab8e83cdc940df82fbf8418e2593f (patch) | |
tree | f1324a39f155375221ed88db0626f61b75c51db6 /sound | |
parent | ASoC: Store DC offset correction for wm_hubs devices in class W mode (diff) | |
parent | ASoC: Include cx20442 to SND_SOC_ALL_CODECS (diff) | |
download | linux-6e1bd1ab1d9ab8e83cdc940df82fbf8418e2593f.tar.xz linux-6e1bd1ab1d9ab8e83cdc940df82fbf8418e2593f.zip |
Merge branch 'for-2.6.37' into for-2.6.38
Diffstat (limited to 'sound')
130 files changed, 7064 insertions, 3626 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index 070aab490191..45a818002d99 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -31,6 +31,7 @@ /* max number of user-defined controls */ #define MAX_USER_CONTROLS 32 +#define MAX_CONTROL_COUNT 1028 struct snd_kctl_ioctl { struct list_head list; /* list of all ioctls */ @@ -195,6 +196,10 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, if (snd_BUG_ON(!control || !control->count)) return NULL; + + if (control->count > MAX_CONTROL_COUNT) + return NULL; + kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); if (kctl == NULL) { snd_printk(KERN_ERR "Cannot allocate control instance\n"); diff --git a/sound/core/init.c b/sound/core/init.c index ec4a50ce5656..2de45fbd70fb 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 f50ebf20df96..822dd56993ca 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 cbe815dfbdc8..6b4b1287b314 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -203,10 +203,16 @@ static char *snd_pcm_format_names[] = { FORMAT(S18_3BE), FORMAT(U18_3LE), FORMAT(U18_3BE), + FORMAT(G723_24), + FORMAT(G723_24_1B), + FORMAT(G723_40), + FORMAT(G723_40_1B), }; const char *snd_pcm_format_name(snd_pcm_format_t format) { + if (format >= ARRAY_SIZE(snd_pcm_format_names)) + return "Unknown"; return snd_pcm_format_names[format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -358,22 +364,24 @@ 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, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - return; + goto unlock; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); @@ -392,20 +400,25 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - return; + goto unlock; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); @@ -415,24 +428,29 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_substream *substream = entry->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_runtime *runtime; struct snd_pcm_status status; int err; + + mutex_lock(&substream->pcm->open_mutex); + runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - return; + goto unlock; } memset(&status, 0, sizeof(status)); err = snd_pcm_status(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - return; + goto unlock; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); @@ -446,6 +464,8 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, snd_iprintf(buffer, "-----\n"); snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); + unlock: + mutex_unlock(&substream->pcm->open_mutex); } #ifdef CONFIG_SND_PCM_XRUN_DEBUG diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e23e0e7ab26f..a1707cca9c66 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -334,11 +334,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, /* delta = "expected next hw_ptr" for in_interrupt != 0 */ delta = runtime->hw_ptr_interrupt + runtime->period_size; if (delta > new_hw_ptr) { - hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) - hw_base = 0; - new_hw_ptr = hw_base + pos; - goto __delta; + /* check for double acknowledged interrupts */ + hdelta = jiffies - runtime->hw_ptr_jiffies; + if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) + hw_base = 0; + new_hw_ptr = hw_base + pos; + goto __delta; + } } } /* new_hw_ptr might be lower than old_hw_ptr in case when */ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a3b2a6479246..8bc7cb3db330 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) @@ -978,6 +980,10 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push) { if (substream->runtime->trigger_master != substream) return 0; + /* some drivers might use hw_ptr to recover from the pause - + update the hw_ptr now */ + if (push) + snd_pcm_update_hw_ptr(substream); /* The jiffies check in snd_pcm_update_hw_ptr*() is done by * a delta betwen the current jiffies, this gives a large enough * delta, effectively to skip the check once. @@ -1988,6 +1994,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) substream->ops->close(substream); substream->hw_opened = 0; } + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); if (substream->pcm_release) { substream->pcm_release(substream); substream->pcm_release = NULL; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index eb68326c37d4..cbbed0db9e56 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -535,13 +535,15 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) { struct snd_rawmidi_file *rfile; struct snd_rawmidi *rmidi; + struct module *module; rfile = file->private_data; rmidi = rfile->rmidi; rawmidi_release_priv(rfile); kfree(rfile); + module = rmidi->card->module; snd_card_file_remove(rmidi->card, file); - module_put(rmidi->card->module); + module_put(module); return 0; } @@ -829,6 +831,8 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)argp)) return -EFAULT; + if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */ + device = SNDRV_RAWMIDI_DEVICES - 1; mutex_lock(®ister_mutex); device = device < 0 ? 0 : device + 1; while (device < SNDRV_RAWMIDI_DEVICES) { diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 685712276ac9..69cd7b3c362d 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -281,13 +281,10 @@ snd_seq_oss_open(struct file *file, int level) return 0; _error: - snd_seq_oss_writeq_delete(dp->writeq); - snd_seq_oss_readq_delete(dp->readq); snd_seq_oss_synth_cleanup(dp); snd_seq_oss_midi_cleanup(dp); - delete_port(dp); delete_seq_queue(dp->queue); - kfree(dp); + delete_port(dp); return rc; } @@ -350,8 +347,10 @@ create_port(struct seq_oss_devinfo *dp) static int delete_port(struct seq_oss_devinfo *dp) { - if (dp->port < 0) + if (dp->port < 0) { + kfree(dp); return 0; + } debug_printk(("delete_port %i\n", dp->port)); return snd_seq_event_port_detach(dp->cseq, dp->port); diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 480c38623da8..c8961165277c 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -74,6 +74,25 @@ config SND_DUMMY To compile this driver as a module, choose M here: the module will be called snd-dummy. +config SND_ALOOP + tristate "Generic loopback driver (PCM)" + select SND_PCM + help + Say 'Y' or 'M' to include support for the PCM loopback device. + This module returns played samples back to the user space using + the standard ALSA PCM device. The devices are routed 0->1 and + 1->0, where first number is the playback PCM device and second + number is the capture device. Module creates two PCM devices and + configured number of substreams (see the pcm_substreams module + parameter). + + The looback device allow time sychronization with an external + timing source using the time shift universal control (+-20% + of system time). + + To compile this driver as a module, choose M here: the module + will be called snd-aloop. + config SND_VIRMIDI tristate "Virtual MIDI soundcard" depends on SND_SEQUENCER diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index d4a07f9ff2c7..1a8440c8b138 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -4,6 +4,7 @@ # snd-dummy-objs := dummy.o +snd-aloop-objs := aloop.o snd-mtpav-objs := mtpav.o snd-mts64-objs := mts64.o snd-portman2x4-objs := portman2x4.o @@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-dummy.o +obj-$(CONFIG_SND_ALOOP) += snd-aloop.o obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c new file mode 100644 index 000000000000..12b44b0b6777 --- /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 0e631c3221e3..f4cd49336f33 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -94,7 +94,7 @@ static int __devinit snd_virmidi_probe(struct platform_device *devptr) sizeof(struct snd_card_virmidi), &card); if (err < 0) return err; - vmidi = (struct snd_card_virmidi *)card->private_data; + vmidi = card->private_data; vmidi->card = card; if (midi_devs[dev] > MAX_MIDI_DEVICES) { diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 1adb8a3c2b62..57ccba88700d 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++) { @@ -900,7 +900,7 @@ static int proc_init(struct snd_akm4xxx *ak) return 0; } #else /* !CONFIG_PROC_FS */ -static int proc_init(struct snd_akm4xxx *ak) {} +static int proc_init(struct snd_akm4xxx *ak) { return 0; } #endif int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index c6990c680796..52064cfa91f3 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -77,6 +77,32 @@ config SND_ALS100 To compile this driver as a module, choose M here: the module will be called snd-als100. +config SND_AZT1605 + tristate "Aztech AZT1605 Driver" + depends on SND + select SND_WSS_LIB + select SND_MPU401_UART + select SND_OPL3_LIB + help + Say Y here to include support for Aztech Sound Galaxy cards + based on the AZT1605 chipset. + + To compile this driver as a module, choose M here: the module + will be called snd-azt1605. + +config SND_AZT2316 + tristate "Aztech AZT2316 Driver" + depends on SND + select SND_WSS_LIB + select SND_MPU401_UART + select SND_OPL3_LIB + help + Say Y here to include support for Aztech Sound Galaxy cards + based on the AZT2316 chipset. + + To compile this driver as a module, choose M here: the module + will be called snd-azt2316. + config SND_AZT2320 tristate "Aztech Systems AZT2320" depends on PNP @@ -351,16 +377,6 @@ config SND_SB16_CSP coprocessor can do variable tasks like various compression and decompression algorithms. -config SND_SGALAXY - tristate "Aztech Sound Galaxy" - select SND_WSS_LIB - help - Say Y here to include support for Aztech Sound Galaxy - soundcards. - - To compile this driver as a module, choose M here: the module - will be called snd-sgalaxy. - config SND_SSCAPE tristate "Ensoniq SoundScape driver" select SND_MPU401_UART diff --git a/sound/isa/Makefile b/sound/isa/Makefile index c73d30c4f462..8d781e419e2e 100644 --- a/sound/isa/Makefile +++ b/sound/isa/Makefile @@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o snd-es18xx-objs := es18xx.o snd-opl3sa2-objs := opl3sa2.o snd-sc6000-objs := sc6000.o -snd-sgalaxy-objs := sgalaxy.o snd-sscape-objs := sscape.o # Toplevel Module Dependency @@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o obj-$(CONFIG_SND_SC6000) += snd-sc6000.o -obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o -obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \ +obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \ sb/ wavefront/ wss/ diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index bbcbf92a8ebe..3cb75bc97699 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -162,7 +162,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard sizeof(struct snd_card_ad1816a), &card); if (error < 0) return error; - acard = (struct snd_card_ad1816a *)card->private_data; + acard = card->private_data; if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) { snd_card_free(card); diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index f7aa637b0d18..aac8dc15c2fe 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -188,7 +188,7 @@ static int __devinit snd_card_azt2320_probe(int dev, sizeof(struct snd_card_azt2320), &card); if (error < 0) return error; - acard = (struct snd_card_azt2320 *)card->private_data; + acard = card->private_data; if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) { snd_card_free(card); diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile new file mode 100644 index 000000000000..e307066d4315 --- /dev/null +++ b/sound/isa/galaxy/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# + +snd-azt1605-objs := azt1605.o +snd-azt2316-objs := azt2316.o + +obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o +obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c new file mode 100644 index 000000000000..9a97643cb713 --- /dev/null +++ b/sound/isa/galaxy/azt1605.c @@ -0,0 +1,91 @@ +/* + * Aztech AZT1605 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#define AZT1605 + +#define CRD_NAME "Aztech AZT1605" +#define DRV_NAME "AZT1605" +#define DEV_NAME "azt1605" + +#define GALAXY_DSP_MAJOR 2 +#define GALAXY_DSP_MINOR 1 + +#define GALAXY_CONFIG_SIZE 3 + +/* + * 24-bit config register + */ + +#define GALAXY_CONFIG_SBA_220 (0 << 0) +#define GALAXY_CONFIG_SBA_240 (1 << 0) +#define GALAXY_CONFIG_SBA_260 (2 << 0) +#define GALAXY_CONFIG_SBA_280 (3 << 0) +#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 + +#define GALAXY_CONFIG_MPUA_300 (0 << 2) +#define GALAXY_CONFIG_MPUA_330 (1 << 2) + +#define GALAXY_CONFIG_MPU_ENABLE (1 << 3) + +#define GALAXY_CONFIG_GAME_ENABLE (1 << 4) + +#define GALAXY_CONFIG_CD_PANASONIC (1 << 5) +#define GALAXY_CONFIG_CD_MITSUMI (1 << 6) +#define GALAXY_CONFIG_CD_MASK (\ + GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI) + +#define GALAXY_CONFIG_UNUSED (1 << 7) +#define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED + +#define GALAXY_CONFIG_SBIRQ_2 (1 << 8) +#define GALAXY_CONFIG_SBIRQ_3 (1 << 9) +#define GALAXY_CONFIG_SBIRQ_5 (1 << 10) +#define GALAXY_CONFIG_SBIRQ_7 (1 << 11) + +#define GALAXY_CONFIG_MPUIRQ_2 (1 << 12) +#define GALAXY_CONFIG_MPUIRQ_3 (1 << 13) +#define GALAXY_CONFIG_MPUIRQ_5 (1 << 14) +#define GALAXY_CONFIG_MPUIRQ_7 (1 << 15) + +#define GALAXY_CONFIG_WSSA_530 (0 << 16) +#define GALAXY_CONFIG_WSSA_604 (1 << 16) +#define GALAXY_CONFIG_WSSA_E80 (2 << 16) +#define GALAXY_CONFIG_WSSA_F40 (3 << 16) + +#define GALAXY_CONFIG_WSS_ENABLE (1 << 18) + +#define GALAXY_CONFIG_CDIRQ_11 (1 << 19) +#define GALAXY_CONFIG_CDIRQ_12 (1 << 20) +#define GALAXY_CONFIG_CDIRQ_15 (1 << 21) +#define GALAXY_CONFIG_CDIRQ_MASK (\ + GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\ + GALAXY_CONFIG_CDIRQ_15) + +#define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22) +#define GALAXY_CONFIG_CDDMA_0 (1 << 22) +#define GALAXY_CONFIG_CDDMA_1 (2 << 22) +#define GALAXY_CONFIG_CDDMA_3 (3 << 22) +#define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3 + +#define GALAXY_CONFIG_MASK (\ + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\ + GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\ + GALAXY_CONFIG_CDDMA_MASK) + +#include "galaxy.c" diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c new file mode 100644 index 000000000000..189441141df6 --- /dev/null +++ b/sound/isa/galaxy/azt2316.c @@ -0,0 +1,111 @@ +/* + * Aztech AZT2316 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#define AZT2316 + +#define CRD_NAME "Aztech AZT2316" +#define DRV_NAME "AZT2316" +#define DEV_NAME "azt2316" + +#define GALAXY_DSP_MAJOR 3 +#define GALAXY_DSP_MINOR 1 + +#define GALAXY_CONFIG_SIZE 4 + +/* + * 32-bit config register + */ + +#define GALAXY_CONFIG_SBA_220 (0 << 0) +#define GALAXY_CONFIG_SBA_240 (1 << 0) +#define GALAXY_CONFIG_SBA_260 (2 << 0) +#define GALAXY_CONFIG_SBA_280 (3 << 0) +#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 + +#define GALAXY_CONFIG_SBIRQ_2 (1 << 2) +#define GALAXY_CONFIG_SBIRQ_5 (1 << 3) +#define GALAXY_CONFIG_SBIRQ_7 (1 << 4) +#define GALAXY_CONFIG_SBIRQ_10 (1 << 5) + +#define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6) +#define GALAXY_CONFIG_SBDMA_0 (1 << 6) +#define GALAXY_CONFIG_SBDMA_1 (2 << 6) +#define GALAXY_CONFIG_SBDMA_3 (3 << 6) + +#define GALAXY_CONFIG_WSSA_530 (0 << 8) +#define GALAXY_CONFIG_WSSA_604 (1 << 8) +#define GALAXY_CONFIG_WSSA_E80 (2 << 8) +#define GALAXY_CONFIG_WSSA_F40 (3 << 8) + +#define GALAXY_CONFIG_WSS_ENABLE (1 << 10) + +#define GALAXY_CONFIG_GAME_ENABLE (1 << 11) + +#define GALAXY_CONFIG_MPUA_300 (0 << 12) +#define GALAXY_CONFIG_MPUA_330 (1 << 12) + +#define GALAXY_CONFIG_MPU_ENABLE (1 << 13) + +#define GALAXY_CONFIG_CDA_310 (0 << 14) +#define GALAXY_CONFIG_CDA_320 (1 << 14) +#define GALAXY_CONFIG_CDA_340 (2 << 14) +#define GALAXY_CONFIG_CDA_350 (3 << 14) +#define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350 + +#define GALAXY_CONFIG_CD_DISABLE (0 << 16) +#define GALAXY_CONFIG_CD_PANASONIC (1 << 16) +#define GALAXY_CONFIG_CD_SONY (2 << 16) +#define GALAXY_CONFIG_CD_MITSUMI (3 << 16) +#define GALAXY_CONFIG_CD_AZTECH (4 << 16) +#define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16) +#define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16) +#define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16) +#define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7 + +#define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20) +#define GALAXY_CONFIG_CDDMA8_0 (1 << 20) +#define GALAXY_CONFIG_CDDMA8_1 (2 << 20) +#define GALAXY_CONFIG_CDDMA8_3 (3 << 20) +#define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3 + +#define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22) +#define GALAXY_CONFIG_CDDMA16_5 (1 << 22) +#define GALAXY_CONFIG_CDDMA16_6 (2 << 22) +#define GALAXY_CONFIG_CDDMA16_7 (3 << 22) +#define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7 + +#define GALAXY_CONFIG_MPUIRQ_2 (1 << 24) +#define GALAXY_CONFIG_MPUIRQ_5 (1 << 25) +#define GALAXY_CONFIG_MPUIRQ_7 (1 << 26) +#define GALAXY_CONFIG_MPUIRQ_10 (1 << 27) + +#define GALAXY_CONFIG_CDIRQ_5 (1 << 28) +#define GALAXY_CONFIG_CDIRQ_11 (1 << 29) +#define GALAXY_CONFIG_CDIRQ_12 (1 << 30) +#define GALAXY_CONFIG_CDIRQ_15 (1 << 31) +#define GALAXY_CONFIG_CDIRQ_MASK (\ + GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\ + GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15) + +#define GALAXY_CONFIG_MASK (\ + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\ + GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\ + GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK) + +#include "galaxy.c" diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c new file mode 100644 index 000000000000..ee54df082b9c --- /dev/null +++ b/sound/isa/galaxy/galaxy.c @@ -0,0 +1,652 @@ +/* + * Aztech AZT1605/AZT2316 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/isa.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <asm/processor.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/wss.h> +#include <sound/mpu401.h> +#include <sound/opl3.h> + +MODULE_DESCRIPTION(CRD_NAME); +MODULE_AUTHOR("Rene Herman"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); + +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +module_param_array(port, long, NULL, 0444); +MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); +module_param_array(wss_port, long, NULL, 0444); +MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver."); +module_param_array(mpu_port, long, NULL, 0444); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); +module_param_array(fm_port, long, NULL, 0444); +MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver."); +module_param_array(irq, int, NULL, 0444); +MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); +module_param_array(mpu_irq, int, NULL, 0444); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); +module_param_array(dma1, int, NULL, 0444); +MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver."); +module_param_array(dma2, int, NULL, 0444); +MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver."); + +/* + * Generic SB DSP support routines + */ + +#define DSP_PORT_RESET 0x6 +#define DSP_PORT_READ 0xa +#define DSP_PORT_COMMAND 0xc +#define DSP_PORT_STATUS 0xc +#define DSP_PORT_DATA_AVAIL 0xe + +#define DSP_SIGNATURE 0xaa + +#define DSP_COMMAND_GET_VERSION 0xe1 + +static int __devinit dsp_get_byte(void __iomem *port, u8 *val) +{ + int loops = 1000; + + while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) { + if (!loops--) + return -EIO; + cpu_relax(); + } + *val = ioread8(port + DSP_PORT_READ); + return 0; +} + +static int __devinit dsp_reset(void __iomem *port) +{ + u8 val; + + iowrite8(1, port + DSP_PORT_RESET); + udelay(10); + iowrite8(0, port + DSP_PORT_RESET); + + if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE) + return -ENODEV; + + return 0; +} + +static int __devinit dsp_command(void __iomem *port, u8 cmd) +{ + int loops = 1000; + + while (ioread8(port + DSP_PORT_STATUS) & 0x80) { + if (!loops--) + return -EIO; + cpu_relax(); + } + iowrite8(cmd, port + DSP_PORT_COMMAND); + return 0; +} + +static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor) +{ + int err; + + err = dsp_command(port, DSP_COMMAND_GET_VERSION); + if (err < 0) + return err; + + err = dsp_get_byte(port, major); + if (err < 0) + return err; + + err = dsp_get_byte(port, minor); + if (err < 0) + return err; + + return 0; +} + +/* + * Generic WSS support routines + */ + +#define WSS_CONFIG_DMA_0 (1 << 0) +#define WSS_CONFIG_DMA_1 (2 << 0) +#define WSS_CONFIG_DMA_3 (3 << 0) +#define WSS_CONFIG_DUPLEX (1 << 2) +#define WSS_CONFIG_IRQ_7 (1 << 3) +#define WSS_CONFIG_IRQ_9 (2 << 3) +#define WSS_CONFIG_IRQ_10 (3 << 3) +#define WSS_CONFIG_IRQ_11 (4 << 3) + +#define WSS_PORT_CONFIG 0 +#define WSS_PORT_SIGNATURE 3 + +#define WSS_SIGNATURE 4 + +static int __devinit wss_detect(void __iomem *wss_port) +{ + if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE) + return -ENODEV; + + return 0; +} + +static void wss_set_config(void __iomem *wss_port, u8 wss_config) +{ + iowrite8(wss_config, wss_port + WSS_PORT_CONFIG); +} + +/* + * Aztech Sound Galaxy specifics + */ + +#define GALAXY_PORT_CONFIG 1024 +#define CONFIG_PORT_SET 4 + +#define DSP_COMMAND_GALAXY_8 8 +#define GALAXY_COMMAND_GET_TYPE 5 + +#define DSP_COMMAND_GALAXY_9 9 +#define GALAXY_COMMAND_WSSMODE 0 +#define GALAXY_COMMAND_SB8MODE 1 + +#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE +#define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE + +struct snd_galaxy { + void __iomem *port; + void __iomem *config_port; + void __iomem *wss_port; + u32 config; + struct resource *res_port; + struct resource *res_config_port; + struct resource *res_wss_port; +}; + +static u32 config[SNDRV_CARDS]; +static u8 wss_config[SNDRV_CARDS]; + +static int __devinit snd_galaxy_match(struct device *dev, unsigned int n) +{ + if (!enable[n]) + return 0; + + switch (port[n]) { + case SNDRV_AUTO_PORT: + dev_err(dev, "please specify port\n"); + return 0; + case 0x220: + config[n] |= GALAXY_CONFIG_SBA_220; + break; + case 0x240: + config[n] |= GALAXY_CONFIG_SBA_240; + break; + case 0x260: + config[n] |= GALAXY_CONFIG_SBA_260; + break; + case 0x280: + config[n] |= GALAXY_CONFIG_SBA_280; + break; + default: + dev_err(dev, "invalid port %#lx\n", port[n]); + return 0; + } + + switch (wss_port[n]) { + case SNDRV_AUTO_PORT: + dev_err(dev, "please specify wss_port\n"); + return 0; + case 0x530: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530; + break; + case 0x604: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604; + break; + case 0xe80: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80; + break; + case 0xf40: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40; + break; + default: + dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]); + return 0; + } + + switch (irq[n]) { + case SNDRV_AUTO_IRQ: + dev_err(dev, "please specify irq\n"); + return 0; + case 7: + wss_config[n] |= WSS_CONFIG_IRQ_7; + break; + case 2: + irq[n] = 9; + case 9: + wss_config[n] |= WSS_CONFIG_IRQ_9; + break; + case 10: + wss_config[n] |= WSS_CONFIG_IRQ_10; + break; + case 11: + wss_config[n] |= WSS_CONFIG_IRQ_11; + break; + default: + dev_err(dev, "invalid IRQ %d\n", irq[n]); + return 0; + } + + switch (dma1[n]) { + case SNDRV_AUTO_DMA: + dev_err(dev, "please specify dma1\n"); + return 0; + case 0: + wss_config[n] |= WSS_CONFIG_DMA_0; + break; + case 1: + wss_config[n] |= WSS_CONFIG_DMA_1; + break; + case 3: + wss_config[n] |= WSS_CONFIG_DMA_3; + break; + default: + dev_err(dev, "invalid playback DMA %d\n", dma1[n]); + return 0; + } + + if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) { + dma2[n] = -1; + goto mpu; + } + + wss_config[n] |= WSS_CONFIG_DUPLEX; + switch (dma2[n]) { + case 0: + break; + case 1: + if (dma1[n] == 0) + break; + default: + dev_err(dev, "invalid capture DMA %d\n", dma2[n]); + return 0; + } + +mpu: + switch (mpu_port[n]) { + case SNDRV_AUTO_PORT: + dev_warn(dev, "mpu_port not specified; not using MPU-401\n"); + mpu_port[n] = -1; + goto fm; + case 0x300: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300; + break; + case 0x330: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330; + break; + default: + dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]); + return 0; + } + + switch (mpu_irq[n]) { + case SNDRV_AUTO_IRQ: + dev_warn(dev, "mpu_irq not specified: using polling mode\n"); + mpu_irq[n] = -1; + break; + case 2: + mpu_irq[n] = 9; + case 9: + config[n] |= GALAXY_CONFIG_MPUIRQ_2; + break; +#ifdef AZT1605 + case 3: + config[n] |= GALAXY_CONFIG_MPUIRQ_3; + break; +#endif + case 5: + config[n] |= GALAXY_CONFIG_MPUIRQ_5; + break; + case 7: + config[n] |= GALAXY_CONFIG_MPUIRQ_7; + break; +#ifdef AZT2316 + case 10: + config[n] |= GALAXY_CONFIG_MPUIRQ_10; + break; +#endif + default: + dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]); + return 0; + } + + if (mpu_irq[n] == irq[n]) { + dev_err(dev, "cannot share IRQ between WSS and MPU-401\n"); + return 0; + } + +fm: + switch (fm_port[n]) { + case SNDRV_AUTO_PORT: + dev_warn(dev, "fm_port not specified: not using OPL3\n"); + fm_port[n] = -1; + break; + case 0x388: + break; + default: + dev_err(dev, "illegal FM port %#lx\n", fm_port[n]); + return 0; + } + + config[n] |= GALAXY_CONFIG_GAME_ENABLE; + return 1; +} + +static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type) +{ + u8 major; + u8 minor; + int err; + + err = dsp_reset(galaxy->port); + if (err < 0) + return err; + + err = dsp_get_version(galaxy->port, &major, &minor); + if (err < 0) + return err; + + if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR) + return -ENODEV; + + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8); + if (err < 0) + return err; + + err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE); + if (err < 0) + return err; + + err = dsp_get_byte(galaxy->port, type); + if (err < 0) + return err; + + return 0; +} + +static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode) +{ + int err; + + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9); + if (err < 0) + return err; + + err = dsp_command(galaxy->port, mode); + if (err < 0) + return err; + +#ifdef AZT1605 + /* + * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again + */ + err = dsp_reset(galaxy->port); + if (err < 0) + return err; +#endif + + return 0; +} + +static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config) +{ + u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET); + int i; + + iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET); + for (i = 0; i < GALAXY_CONFIG_SIZE; i++) { + iowrite8(config, galaxy->config_port + i); + config >>= 8; + } + iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET); + msleep(10); +} + +static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config) +{ + int i; + + for (i = GALAXY_CONFIG_SIZE; i; i--) { + u8 tmp = ioread8(galaxy->config_port + i - 1); + galaxy->config = (galaxy->config << 8) | tmp; + } + config |= galaxy->config & GALAXY_CONFIG_MASK; + galaxy_set_config(galaxy, config); +} + +static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config) +{ + int err; + + err = wss_detect(galaxy->wss_port); + if (err < 0) + return err; + + wss_set_config(galaxy->wss_port, wss_config); + + err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS); + if (err < 0) + return err; + + return 0; +} + +static void snd_galaxy_free(struct snd_card *card) +{ + struct snd_galaxy *galaxy = card->private_data; + + if (galaxy->wss_port) { + wss_set_config(galaxy->wss_port, 0); + ioport_unmap(galaxy->wss_port); + release_and_free_resource(galaxy->res_wss_port); + } + if (galaxy->config_port) { + galaxy_set_config(galaxy, galaxy->config); + ioport_unmap(galaxy->config_port); + release_and_free_resource(galaxy->res_config_port); + } + if (galaxy->port) { + ioport_unmap(galaxy->port); + release_and_free_resource(galaxy->res_port); + } +} + +static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n) +{ + struct snd_galaxy *galaxy; + struct snd_wss *chip; + struct snd_card *card; + u8 type; + int err; + + err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy, + &card); + if (err < 0) + return err; + + snd_card_set_dev(card, dev); + + card->private_free = snd_galaxy_free; + galaxy = card->private_data; + + galaxy->res_port = request_region(port[n], 16, DRV_NAME); + if (!galaxy->res_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n], + port[n] + 15); + err = -EBUSY; + goto error; + } + galaxy->port = ioport_map(port[n], 16); + + err = galaxy_init(galaxy, &type); + if (err < 0) { + dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]); + goto error; + } + dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]); + + galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG, + 16, DRV_NAME); + if (!galaxy->res_config_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", + port[n] + GALAXY_PORT_CONFIG, + port[n] + GALAXY_PORT_CONFIG + 15); + err = -EBUSY; + goto error; + } + galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16); + + galaxy_config(galaxy, config[n]); + + galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME); + if (!galaxy->res_wss_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n], + wss_port[n] + 3); + err = -EBUSY; + goto error; + } + galaxy->wss_port = ioport_map(wss_port[n], 4); + + err = galaxy_wss_config(galaxy, wss_config[n]); + if (err < 0) { + dev_err(dev, "could not configure WSS\n"); + goto error; + } + + strcpy(card->driver, DRV_NAME); + strcpy(card->shortname, DRV_NAME); + sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", + card->shortname, port[n], wss_port[n], irq[n], dma1[n], + dma2[n]); + + err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n], + dma2[n], WSS_HW_DETECT, 0, &chip); + if (err < 0) + goto error; + + err = snd_wss_pcm(chip, 0, NULL); + if (err < 0) + goto error; + + err = snd_wss_mixer(chip); + if (err < 0) + goto error; + + err = snd_wss_timer(chip, 0, NULL); + if (err < 0) + goto error; + + if (mpu_port[n] >= 0) { + err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port[n], 0, mpu_irq[n], + IRQF_DISABLED, NULL); + if (err < 0) + goto error; + } + + if (fm_port[n] >= 0) { + struct snd_opl3 *opl3; + + err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2, + OPL3_HW_AUTO, 0, &opl3); + if (err < 0) { + dev_err(dev, "no OPL device at %#lx\n", fm_port[n]); + goto error; + } + err = snd_opl3_timer_new(opl3, 1, 2); + if (err < 0) + goto error; + + err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); + if (err < 0) + goto error; + } + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(dev, card); + return 0; + +error: + snd_card_free(card); + return err; +} + +static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n) +{ + snd_card_free(dev_get_drvdata(dev)); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct isa_driver snd_galaxy_driver = { + .match = snd_galaxy_match, + .probe = snd_galaxy_probe, + .remove = __devexit_p(snd_galaxy_remove), + + .driver = { + .name = DEV_NAME + } +}; + +static int __init alsa_card_galaxy_init(void) +{ + return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS); +} + +static void __exit alsa_card_galaxy_exit(void) +{ + isa_unregister_driver(&snd_galaxy_driver); +} + +module_init(alsa_card_galaxy_init); +module_exit(alsa_card_galaxy_exit); diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index f26eac8d8110..3e4a58b72913 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -191,7 +191,7 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip) static void snd_gusmax_free(struct snd_card *card) { - struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; + struct snd_gusmax *maxcard = card->private_data; if (maxcard == NULL) return; @@ -219,7 +219,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev) if (err < 0) return err; card->private_free = snd_gusmax_free; - maxcard = (struct snd_gusmax *)card->private_data; + maxcard = card->private_data; maxcard->card = card; maxcard->irq = -1; diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 5f3e68401f90..91d6023a63e5 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -764,9 +764,9 @@ static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; static long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#ifndef MSND_CLASSIC static long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; -#ifndef MSND_CLASSIC /* Extra Peripheral Configuration (Default: Disable) */ static long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; static long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; @@ -894,7 +894,11 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx) struct snd_card *card; struct snd_msnd *chip; - if (has_isapnp(idx) || cfg[idx] == SNDRV_AUTO_PORT) { + if (has_isapnp(idx) +#ifndef MSND_CLASSIC + || cfg[idx] == SNDRV_AUTO_PORT +#endif + ) { printk(KERN_INFO LOGNAME ": Assuming PnP mode\n"); return -ENODEV; } diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c index 81284a8fa0ce..2259e3f726a7 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -72,7 +72,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id) static void snd_sb8_free(struct snd_card *card) { - struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; + struct snd_sb8 *acard = card->private_data; if (acard == NULL) return; diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c deleted file mode 100644 index 6fe27b9d9440..000000000000 --- a/sound/isa/sgalaxy.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Driver for Aztech Sound Galaxy cards - * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk. - * - * I don't have documentation for this card, I based this driver on the - * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/init.h> -#include <linux/err.h> -#include <linux/isa.h> -#include <linux/delay.h> -#include <linux/time.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <asm/dma.h> -#include <sound/core.h> -#include <sound/sb.h> -#include <sound/wss.h> -#include <sound/control.h> -#define SNDRV_LEGACY_FIND_FREE_IRQ -#define SNDRV_LEGACY_FIND_FREE_DMA -#include <sound/initval.h> - -MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>"); -MODULE_DESCRIPTION("Aztech Sound Galaxy"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ -static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ -static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ -static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ -static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); -module_param_array(sbport, long, NULL, 0444); -MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); -module_param_array(wssport, long, NULL, 0444); -MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); -module_param_array(dma1, int, NULL, 0444); -MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); - -#define SGALAXY_AUXC_LEFT 18 -#define SGALAXY_AUXC_RIGHT 19 - -#define PFX "sgalaxy: " - -/* - - */ - -#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) - -/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */ -/* short time we actually need it.. */ - -static int snd_sgalaxy_sbdsp_reset(unsigned long port) -{ - int i; - - outb(1, SBP1(port, RESET)); - udelay(10); - outb(0, SBP1(port, RESET)); - udelay(30); - for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); - if (inb(SBP1(port, READ)) != 0xaa) { - snd_printd("sb_reset: failed at 0x%lx!!!\n", port); - return -ENODEV; - } - return 0; -} - -static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port, - unsigned char val) -{ - int i; - - for (i = 10000; i; i--) - if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { - outb(val, SBP1(port, COMMAND)); - return 1; - } - - return 0; -} - -static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id) -{ - return IRQ_NONE; -} - -static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) -{ - static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, - 0x10, 0x18, 0x20, -1, -1, -1, -1}; - static int dma_bits[] = {1, 2, 0, 3}; - int tmp, tmp1; - - if ((tmp = inb(port + 3)) == 0xff) - { - snd_printdd("I/O address dead (0x%lx)\n", port); - return 0; - } -#if 0 - snd_printdd("WSS signature = 0x%x\n", tmp); -#endif - - if ((tmp & 0x3f) != 0x04 && - (tmp & 0x3f) != 0x0f && - (tmp & 0x3f) != 0x00) { - snd_printdd("No WSS signature detected on port 0x%lx\n", - port + 3); - return 0; - } - -#if 0 - snd_printdd(PFX "setting up IRQ/DMA for WSS\n"); -#endif - - /* initialize IRQ for WSS codec */ - tmp = interrupt_bits[irq % 16]; - if (tmp < 0) - return -EINVAL; - - if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) { - snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); - return -EIO; - } - - outb(tmp | 0x40, port); - tmp1 = dma_bits[dma % 4]; - outb(tmp | tmp1, port); - - free_irq(irq, NULL); - - return 0; -} - -static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma) -{ -#if 0 - snd_printdd(PFX "switching to WSS mode\n"); -#endif - - /* switch to WSS mode */ - snd_sgalaxy_sbdsp_reset(sbport[dev]); - - snd_sgalaxy_sbdsp_command(sbport[dev], 9); - snd_sgalaxy_sbdsp_command(sbport[dev], 0); - - udelay(400); - return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); -} - -static struct snd_kcontrol_new snd_sgalaxy_controls[] = { -WSS_DOUBLE("Aux Playback Switch", 0, - SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), -WSS_DOUBLE("Aux Playback Volume", 0, - SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) -}; - -static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip) -{ - struct snd_card *card = chip->card; - struct snd_ctl_elem_id id1, id2; - unsigned int idx; - int err; - - memset(&id1, 0, sizeof(id1)); - memset(&id2, 0, sizeof(id2)); - id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - /* reassign AUX0 to LINE */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Line Playback Switch"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Line Playback Volume"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - /* reassign AUX1 to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - /* build AUX2 input */ - for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_sgalaxy_controls[idx], chip)); - if (err < 0) - return err; - } - return 0; -} - -static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev) -{ - if (!enable[dev]) - return 0; - if (sbport[dev] == SNDRV_AUTO_PORT) { - snd_printk(KERN_ERR PFX "specify SB port\n"); - return 0; - } - if (wssport[dev] == SNDRV_AUTO_PORT) { - snd_printk(KERN_ERR PFX "specify WSS port\n"); - return 0; - } - return 1; -} - -static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev) -{ - static int possible_irqs[] = {7, 9, 10, 11, -1}; - static int possible_dmas[] = {1, 3, 0, -1}; - int err, xirq, xdma1; - struct snd_card *card; - struct snd_wss *chip; - - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); - if (err < 0) - return err; - - xirq = irq[dev]; - if (xirq == SNDRV_AUTO_IRQ) { - if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { - snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); - err = -EBUSY; - goto _err; - } - } - xdma1 = dma1[dev]; - if (xdma1 == SNDRV_AUTO_DMA) { - if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { - snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); - err = -EBUSY; - goto _err; - } - } - - if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) - goto _err; - - err = snd_wss_create(card, wssport[dev] + 4, -1, - xirq, xdma1, -1, - WSS_HW_DETECT, 0, &chip); - if (err < 0) - goto _err; - card->private_data = chip; - - err = snd_wss_pcm(chip, 0, NULL); - if (err < 0) { - snd_printdd(PFX "error creating new WSS PCM device\n"); - goto _err; - } - err = snd_wss_mixer(chip); - if (err < 0) { - snd_printdd(PFX "error creating new WSS mixer\n"); - goto _err; - } - if ((err = snd_sgalaxy_mixer(chip)) < 0) { - snd_printdd(PFX "the mixer rewrite failed\n"); - goto _err; - } - - strcpy(card->driver, "Sound Galaxy"); - strcpy(card->shortname, "Sound Galaxy"); - sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", - wssport[dev], xirq, xdma1); - - snd_card_set_dev(card, devptr); - - if ((err = snd_card_register(card)) < 0) - goto _err; - - dev_set_drvdata(devptr, card); - return 0; - - _err: - snd_card_free(card); - return err; -} - -static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev) -{ - snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); - return 0; -} - -#ifdef CONFIG_PM -static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n, - pm_message_t state) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct snd_wss *chip = card->private_data; - - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - chip->suspend(chip); - return 0; -} - -static int snd_sgalaxy_resume(struct device *pdev, unsigned int n) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct snd_wss *chip = card->private_data; - - chip->resume(chip); - snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); - snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]); - - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - return 0; -} -#endif - -#define DEV_NAME "sgalaxy" - -static struct isa_driver snd_sgalaxy_driver = { - .match = snd_sgalaxy_match, - .probe = snd_sgalaxy_probe, - .remove = __devexit_p(snd_sgalaxy_remove), -#ifdef CONFIG_PM - .suspend = snd_sgalaxy_suspend, - .resume = snd_sgalaxy_resume, -#endif - .driver = { - .name = DEV_NAME - }, -}; - -static int __init alsa_card_sgalaxy_init(void) -{ - return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS); -} - -static void __exit alsa_card_sgalaxy_exit(void) -{ - isa_unregister_driver(&snd_sgalaxy_driver); -} - -module_init(alsa_card_sgalaxy_init) -module_exit(alsa_card_sgalaxy_exit) diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index a513651fa149..76c090218073 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 567b8a74178a..96f14dcd0cd1 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 c6f2621221ba..a8f626d99c5b 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 6ecd41abb066..87e2c72651f5 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 2e48b17667d0..b4c1eb504c22 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/sb_ess.c b/sound/oss/sb_ess.c index 51a3d381a59e..9890cf2066ff 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -1722,7 +1722,6 @@ printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask); right = (value & 0x0000ff00) >> 8; } else { /* Turn it off (3) */ left = 0; - left = 0; right = 0; } sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right); diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c deleted file mode 100644 index 479e3025a8a3..000000000000 --- 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/sound_timer.c b/sound/oss/sound_timer.c index f0f0c19fbff7..48cda6c4c257 100644 --- a/sound/oss/sound_timer.c +++ b/sound/oss/sound_timer.c @@ -26,7 +26,7 @@ static unsigned long prev_event_time; static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ static struct sound_lowlev_timer *tmr; -static spinlock_t lock; +static DEFINE_SPINLOCK(lock); static unsigned long tmr2ticks(int tmr_value) { diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index 92aa762ffb7e..46c0d03dbecc 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; } @@ -391,15 +392,15 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: - return audio_ioctl(dev, file, cmd, p); + ret = audio_ioctl(dev, file, cmd, p); break; case SND_DEV_MIDIN: - return MIDIbuf_ioctl(dev, file, cmd, p); + ret = MIDIbuf_ioctl(dev, file, cmd, p); 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 b15840ad2527..44357d877a27 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 8cd73cdd88af..643f1113b1d8 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 e7a8cd058efb..12e34653b8a8 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -207,12 +207,12 @@ config SND_CMIPCI config SND_OXYGEN_LIB tristate - select SND_PCM - select SND_MPU401_UART config SND_OXYGEN tristate "C-Media 8788 (Oxygen)" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART help Say Y here to include support for sound cards based on the C-Media CMI8788 (Oxygen HD Audio) chip: @@ -581,6 +581,8 @@ config SND_HDSPM config SND_HIFIER tristate "TempoTec HiFier Fantasia" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART help Say Y here to include support for the MediaTek/TempoTec HiFier Fantasia sound card. @@ -815,14 +817,17 @@ config SND_VIA82XX_MODEM will be called snd-via82xx-modem. config SND_VIRTUOSO - tristate "Asus Virtuoso 100/200 (Xonar)" + tristate "Asus Virtuoso 66/100/200 (Xonar)" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART + select SND_JACK if INPUT=y || INPUT=SND help Say Y here to include support for sound cards based on the - Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, + Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), and Essence STX. - Support for the DS is experimental. - Support for the HDAV1.3 (Deluxe) is very experimental. + Support for the HDAV1.3 (Deluxe) is incomplete; for the + HDAV1.3 Slim and Xense, missing. To compile this driver as a module, choose M here: the module will be called snd-virtuoso. diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 3b4413448226..22c5fc625533 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -941,8 +941,7 @@ static void outstream_host_buffer_free(struct hpi_adapter_obj *pao, } -static u32 outstream_get_space_available(struct hpi_hostbuffer_status - *status) +static u32 outstream_get_space_available(struct hpi_hostbuffer_status *status) { return status->size_in_bytes - (status->host_index - status->dSP_index); @@ -987,6 +986,10 @@ static void outstream_write(struct hpi_adapter_obj *pao, /* write it */ phm->function = HPI_OSTREAM_WRITE; hw_message(pao, phm, phr); + + if (phr->error) + return; + /* update status information that the DSP would typically * update (and will update next time the DSP * buffer update task reads data from the host BBM buffer) diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index c92f493d341e..557c782ae4fc 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 14b8d9a91aae..f19c11077255 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 0a3d3d6e77b4..d2d12c08f937 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 85fd315d9999..630aa4998189 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/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 4203782d7cb7..aff8387c45cf 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -52,6 +52,7 @@ static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; static int enable_ir[SNDRV_CARDS]; static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */ +static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); @@ -73,6 +74,8 @@ module_param_array(enable_ir, bool, NULL, 0444); MODULE_PARM_DESC(enable_ir, "Enable IR."); module_param_array(subsystem, uint, NULL, 0444); MODULE_PARM_DESC(subsystem, "Force card subsystem model."); +module_param_array(delay_pcm_irq, uint, NULL, 0444); +MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0)."); /* * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400 */ @@ -127,6 +130,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, &emu)) < 0) goto error; card->private_data = emu; + emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f; if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) goto error; if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 8578c70c61f2..bab564824efe 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -321,7 +321,7 @@ static struct snd_rawmidi_ops snd_emu10k1_midi_input = static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi) { - struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data; + struct snd_emu10k1_midi *midi = rmidi->private_data; midi->interrupt = NULL; midi->rmidi = NULL; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 55b83ef73c63..622bace148e3 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -332,7 +332,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, evoice->epcm->ccca_start_addr = start_addr + ccis; if (extra) { start_addr += ccis; - end_addr += ccis; + end_addr += ccis + emu->delay_pcm_irq; } if (stereo && !extra) { snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); @@ -360,7 +360,9 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, /* Assumption that PT is already 0 so no harm overwriting */ snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); - snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); + snd_emu10k1_ptr_write(emu, PSST, voice, + (start_addr + (extra ? emu->delay_pcm_irq : 0)) | + (send_amount[2] << 24)); if (emu->card_capabilities->emu_model) pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ else @@ -732,6 +734,23 @@ static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_ snd_emu10k1_ptr_write(emu, IP, voice, 0); } +static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu, + struct snd_emu10k1_pcm *epcm, + struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime) +{ + unsigned int ptr, period_pos; + + /* try to sychronize the current position for the interrupt + source voice */ + period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt; + period_pos %= runtime->period_size; + ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number); + ptr &= ~0x00ffffff; + ptr |= epcm->ccca_start_addr + period_pos; + snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr); +} + static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -753,6 +772,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, /* follow thru */ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) + snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime); mix = &emu->pcm_mixer[substream->number]; snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix); snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix); @@ -869,8 +890,9 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream * #endif /* printk(KERN_DEBUG - "ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", - ptr, runtime->buffer_size, runtime->period_size); + "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n", + (long)ptr, (long)runtime->buffer_size, + (long)runtime->period_size); */ return ptr; } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index ffb1ddb8dc28..957a311514c8 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -310,8 +310,10 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst if (snd_BUG_ON(!hdr)) return NULL; + idx = runtime->period_size >= runtime->buffer_size ? + (emu->delay_pcm_irq * 2) : 0; mutex_lock(&hdr->block_mutex); - blk = search_empty(emu, runtime->dma_bytes); + blk = search_empty(emu, runtime->dma_bytes + idx); if (blk == NULL) { mutex_unlock(&hdr->block_mutex); return NULL; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 9194c3c1d04a..0ea5cc60ac78 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA snd-hda-codec-via. This module is automatically loaded at probing. -config SND_HDA_CODEC_ATIHDMI - bool "Build ATI HDMI HD-audio codec support" - default y - help - Say Y here to include ATI HDMI HD-audio codec support in - snd-hda-intel driver, such as ATI RS600 HDMI. - - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-atihdmi. - This module is automatically loaded at probing. - -config SND_HDA_CODEC_NVHDMI - bool "Build NVIDIA HDMI HD-audio codec support" - default y - help - Say Y here to include NVIDIA HDMI HD-audio codec support in - snd-hda-intel driver, such as NVIDIA MCP78 HDMI. - - When the HD-audio driver is built as a module, the codec - support code is also built as another module, - snd-hda-codec-nvhdmi. - This module is automatically loaded at probing. - -config SND_HDA_CODEC_INTELHDMI - bool "Build INTEL HDMI HD-audio codec support" +config SND_HDA_CODEC_HDMI + bool "Build HDMI/DisplayPort HD-audio codec support" select SND_DYNAMIC_MINORS default y help - Say Y here to include INTEL HDMI HD-audio codec support in - snd-hda-intel driver, such as Eaglelake integrated HDMI. + Say Y here to include HDMI and DisplayPort HD-audio codec + support in snd-hda-intel driver. This includes all AMD/ATI, + Intel and Nvidia HDMI/DisplayPort codecs. When the HD-audio driver is built as a module, the codec support code is also built as another module, - snd-hda-codec-intelhdmi. + snd-hda-codec-hdmi. This module is automatically loaded at probing. -config SND_HDA_ELD - def_bool y - depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI - config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" depends on SND_HDA_INTEL diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24bc195b02da..17ef3658f34b 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o snd-hda-codec-y := hda_codec.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o -snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o @@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs := patch_cmedia.o snd-hda-codec-analog-objs := patch_analog.o snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o -snd-hda-codec-atihdmi-objs := patch_atihdmi.o snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o -snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o -snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o +snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o # common driver obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o @@ -39,9 +36,6 @@ endif ifdef CONFIG_SND_HDA_CODEC_SI3054 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o endif -ifdef CONFIG_SND_HDA_CODEC_ATIHDMI -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o -endif ifdef CONFIG_SND_HDA_CODEC_CIRRUS obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o endif @@ -54,11 +48,8 @@ endif ifdef CONFIG_SND_HDA_CODEC_VIA obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o endif -ifdef CONFIG_SND_HDA_CODEC_NVHDMI -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o -endif -ifdef CONFIG_SND_HDA_CODEC_INTELHDMI -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o +ifdef CONFIG_SND_HDA_CODEC_HDMI +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o endif # this must be the last entry after codec drivers; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index dd8fb86c842b..644e3f14f8ca 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -589,6 +589,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, bus->ops = temp->ops; mutex_init(&bus->cmd_mutex); + mutex_init(&bus->prepare_mutex); INIT_LIST_HEAD(&bus->codec_list); snprintf(bus->workq_name, sizeof(bus->workq_name), @@ -1068,7 +1069,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); - mutex_init(&codec->prepare_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); @@ -1213,8 +1213,10 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format) { + struct hda_codec *c; struct hda_cvt_setup *p; unsigned int oldval, newval; + int type; int i; if (!nid) @@ -1253,10 +1255,14 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, p->dirty = 0; /* make other inactive cvts with the same stream-tag dirty */ - for (i = 0; i < codec->cvt_setups.used; i++) { - p = snd_array_elem(&codec->cvt_setups, i); - if (!p->active && p->stream_tag == stream_tag) - p->dirty = 1; + type = get_wcaps_type(get_wcaps(codec, nid)); + list_for_each_entry(c, &codec->bus->codec_list, list) { + for (i = 0; i < c->cvt_setups.used; i++) { + p = snd_array_elem(&c->cvt_setups, i); + if (!p->active && p->stream_tag == stream_tag && + get_wcaps_type(get_wcaps(codec, p->nid)) == type) + p->dirty = 1; + } } } EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); @@ -1278,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, if (!nid) return; + if (codec->no_sticky_stream) + do_now = 1; + snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); if (p) { @@ -1306,12 +1315,16 @@ static void really_cleanup_stream(struct hda_codec *codec, /* clean up the all conflicting obsolete streams */ static void purify_inactive_streams(struct hda_codec *codec) { + struct hda_codec *c; int i; - for (i = 0; i < codec->cvt_setups.used; i++) { - struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); - if (p->dirty) - really_cleanup_stream(codec, p); + list_for_each_entry(c, &codec->bus->codec_list, list) { + for (i = 0; i < c->cvt_setups.used; i++) { + struct hda_cvt_setup *p; + p = snd_array_elem(&c->cvt_setups, i); + if (p->dirty) + really_cleanup_stream(c, p); + } } } @@ -1824,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, hda_nid_t nid = get_amp_nid(kcontrol); int dir = get_amp_direction(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); + bool min_mute = get_amp_min_mute(kcontrol); u32 caps, val1, val2; if (size < 4 * sizeof(unsigned int)) @@ -1834,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 += ofs; val1 = ((int)val1) * ((int)val2); + if (min_mute) + val2 |= TLV_DB_SCALE_MUTE; if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) return -EFAULT; if (put_user(2 * sizeof(unsigned int), _tlv + 1)) @@ -2221,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (codec->patch_ops.check_power_status) - codec->patch_ops.check_power_status(codec, nid); -#endif + hda_call_check_power_status(codec, nid); snd_hda_power_down(codec); return change; } @@ -3502,11 +3515,11 @@ int snd_hda_codec_prepare(struct hda_codec *codec, struct snd_pcm_substream *substream) { int ret; - mutex_lock(&codec->prepare_mutex); + mutex_lock(&codec->bus->prepare_mutex); ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); if (ret >= 0) purify_inactive_streams(codec); - mutex_unlock(&codec->prepare_mutex); + mutex_unlock(&codec->bus->prepare_mutex); return ret; } EXPORT_SYMBOL_HDA(snd_hda_codec_prepare); @@ -3515,9 +3528,9 @@ void snd_hda_codec_cleanup(struct hda_codec *codec, struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { - mutex_lock(&codec->prepare_mutex); + mutex_lock(&codec->bus->prepare_mutex); hinfo->ops.cleanup(hinfo, codec, substream); - mutex_unlock(&codec->prepare_mutex); + mutex_unlock(&codec->bus->prepare_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup); @@ -4365,6 +4378,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, } +/* add the found input-pin to the cfg->inputs[] table */ +static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, + int type) +{ + if (cfg->num_inputs < AUTO_CFG_MAX_INS) { + cfg->inputs[cfg->num_inputs].pin = nid; + cfg->inputs[cfg->num_inputs].type = type; + cfg->num_inputs++; + } +} + +/* sort inputs in the order of AUTO_PIN_* type */ +static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) +{ + int i, j; + + for (i = 0; i < cfg->num_inputs; i++) { + for (j = i + 1; j < cfg->num_inputs; j++) { + if (cfg->inputs[i].type > cfg->inputs[j].type) { + struct auto_pin_cfg_item tmp; + tmp = cfg->inputs[i]; + cfg->inputs[i] = cfg->inputs[j]; + cfg->inputs[j] = tmp; + } + } + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -4378,7 +4419,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, * output, i.e. to line_out_pins[0]. So, line_outs is always positive * if any analog output exists. * - * The analog input pins are assigned to input_pins array. + * The analog input pins are assigned to inputs array. * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, * respectively. */ @@ -4391,6 +4432,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; + int i; memset(cfg, 0, sizeof(*cfg)); @@ -4461,33 +4503,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; cfg->hp_outs++; break; - case AC_JACK_MIC_IN: { - int preferred, alt; - if (loc == AC_JACK_LOC_FRONT || - (loc & 0x30) == AC_JACK_LOC_INTERNAL) { - preferred = AUTO_PIN_FRONT_MIC; - alt = AUTO_PIN_MIC; - } else { - preferred = AUTO_PIN_MIC; - alt = AUTO_PIN_FRONT_MIC; - } - if (!cfg->input_pins[preferred]) - cfg->input_pins[preferred] = nid; - else if (!cfg->input_pins[alt]) - cfg->input_pins[alt] = nid; + case AC_JACK_MIC_IN: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); break; - } case AC_JACK_LINE_IN: - if (loc == AC_JACK_LOC_FRONT) - cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; - else - cfg->input_pins[AUTO_PIN_LINE] = nid; + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); break; case AC_JACK_CD: - cfg->input_pins[AUTO_PIN_CD] = nid; + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); break; case AC_JACK_AUX: - cfg->input_pins[AUTO_PIN_AUX] = nid; + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); break; case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: @@ -4529,9 +4555,11 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->hp_outs--; memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); - memmove(sequences_hp + i - 1, sequences_hp + i, + memmove(sequences_hp + i, sequences_hp + i + 1, sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); } + memset(cfg->hp_pins + cfg->hp_outs, 0, + sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); } /* sort by sequence */ @@ -4542,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, sort_pins_by_sequence(cfg->hp_pins, sequences_hp, cfg->hp_outs); - /* if we have only one mic, make it AUTO_PIN_MIC */ - if (!cfg->input_pins[AUTO_PIN_MIC] && - cfg->input_pins[AUTO_PIN_FRONT_MIC]) { - cfg->input_pins[AUTO_PIN_MIC] = - cfg->input_pins[AUTO_PIN_FRONT_MIC]; - cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0; - } - /* ditto for line-in */ - if (!cfg->input_pins[AUTO_PIN_LINE] && - cfg->input_pins[AUTO_PIN_FRONT_LINE]) { - cfg->input_pins[AUTO_PIN_LINE] = - cfg->input_pins[AUTO_PIN_FRONT_LINE]; - cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0; - } - /* * FIX-UP: if no line-outs are detected, try to use speaker or HP pin * as a primary output @@ -4595,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, break; } + sort_autocfg_input_pins(cfg); + /* * debug prints of the parsed results */ @@ -4614,14 +4629,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, if (cfg->dig_outs) snd_printd(" dig-out=0x%x/0x%x\n", cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," - " cd=0x%x, aux=0x%x\n", - cfg->input_pins[AUTO_PIN_MIC], - cfg->input_pins[AUTO_PIN_FRONT_MIC], - cfg->input_pins[AUTO_PIN_LINE], - cfg->input_pins[AUTO_PIN_FRONT_LINE], - cfg->input_pins[AUTO_PIN_CD], - cfg->input_pins[AUTO_PIN_AUX]); + snd_printd(" inputs:"); + for (i = 0; i < cfg->num_inputs; i++) { + snd_printdd(" %s=0x%x", + hda_get_autocfg_input_label(codec, cfg, i), + cfg->inputs[i].pin); + } + snd_printd("\n"); if (cfg->dig_in_pin) snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); @@ -4629,11 +4643,165 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config); -/* labels for input pins */ -const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" -}; -EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); +int snd_hda_get_input_pin_attr(unsigned int def_conf) +{ + unsigned int loc = get_defcfg_location(def_conf); + unsigned int conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + return INPUT_PIN_ATTR_UNUSED; + /* Windows may claim the internal mic to be BOTH, too */ + if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) + return INPUT_PIN_ATTR_DOCK; + if (loc == AC_JACK_LOC_REAR) + return INPUT_PIN_ATTR_REAR; + if (loc == AC_JACK_LOC_FRONT) + return INPUT_PIN_ATTR_FRONT; + return INPUT_PIN_ATTR_NORMAL; +} +EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); + +/** + * hda_get_input_pin_label - Give a label for the given input pin + * + * When check_location is true, the function checks the pin location + * for mic and line-in pins, and set an appropriate prefix like "Front", + * "Rear", "Internal". + */ + +const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin, + int check_location) +{ + unsigned int def_conf; + static const char *mic_names[] = { + "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + }; + int attr; + + def_conf = snd_hda_codec_get_pincfg(codec, pin); + + switch (get_defcfg_device(def_conf)) { + case AC_JACK_MIC_IN: + if (!check_location) + return "Mic"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + return mic_names[attr - 1]; + case AC_JACK_LINE_IN: + if (!check_location) + return "Line"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + if (attr == INPUT_PIN_ATTR_DOCK) + return "Dock Line"; + return "Line"; + case AC_JACK_AUX: + return "Aux"; + case AC_JACK_CD: + return "CD"; + case AC_JACK_SPDIF_IN: + return "SPDIF In"; + case AC_JACK_DIG_OTHER_IN: + return "Digital In"; + default: + return "Misc"; + } +} +EXPORT_SYMBOL_HDA(hda_get_input_pin_label); + +/* Check whether the location prefix needs to be added to the label. + * If all mic-jacks are in the same location (e.g. rear panel), we don't + * have to put "Front" prefix to each label. In such a case, returns false. + */ +static int check_mic_location_need(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + unsigned int defc; + int i, attr, attr2; + + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); + attr = snd_hda_get_input_pin_attr(defc); + /* for internal or docking mics, we need locations */ + if (attr <= INPUT_PIN_ATTR_NORMAL) + return 1; + + attr = 0; + for (i = 0; i < cfg->num_inputs; i++) { + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); + attr2 = snd_hda_get_input_pin_attr(defc); + if (attr2 >= INPUT_PIN_ATTR_NORMAL) { + if (attr && attr != attr2) + return 1; /* different locations found */ + attr = attr2; + } + } + return 0; +} + +/** + * hda_get_autocfg_input_label - Get a label for the given input + * + * Get a label for the given input pin defined by the autocfg item. + * Unlike hda_get_input_pin_label(), this function checks all inputs + * defined in autocfg and avoids the redundant mic/line prefix as much as + * possible. + */ +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + int type = cfg->inputs[input].type; + int has_multiple_pins = 0; + + if ((input > 0 && cfg->inputs[input - 1].type == type) || + (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) + has_multiple_pins = 1; + if (has_multiple_pins && type == AUTO_PIN_MIC) + has_multiple_pins &= check_mic_location_need(codec, cfg, input); + return hda_get_input_pin_label(codec, cfg->inputs[input].pin, + has_multiple_pins); +} +EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); + +/** + * snd_hda_add_imux_item - Add an item to input_mux + * + * When the same label is used already in the existing items, the number + * suffix is appended to the label. This label index number is stored + * to type_idx when non-NULL pointer is given. + */ +int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, + int index, int *type_idx) +{ + int i, label_idx = 0; + if (imux->num_items >= HDA_MAX_NUM_INPUTS) { + snd_printd(KERN_ERR "hda_codec: Too many imux items!\n"); + return -EINVAL; + } + for (i = 0; i < imux->num_items; i++) { + if (!strncmp(label, imux->items[i].label, strlen(label))) + label_idx++; + } + if (type_idx) + *type_idx = label_idx; + if (label_idx > 0) + snprintf(imux->items[imux->num_items].label, + sizeof(imux->items[imux->num_items].label), + "%s %d", label, label_idx); + else + strlcpy(imux->items[imux->num_items].label, label, + sizeof(imux->items[imux->num_items].label)); + imux->items[imux->num_items].index = index; + imux->num_items++; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_add_imux_item); #ifdef CONFIG_PM diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 4303353feda9..fdf8d44f8b6b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -648,6 +648,7 @@ struct hda_bus { struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; struct mutex cmd_mutex; + struct mutex prepare_mutex; /* unsolicited event queue */ struct hda_bus_unsolicited *unsol; @@ -826,7 +827,6 @@ struct hda_codec { struct mutex spdif_mutex; struct mutex control_mutex; - struct mutex prepare_mutex; unsigned int spdif_status; /* IEC958 status bits */ unsigned short spdif_ctls; /* SPDIF control bits */ unsigned int spdif_in_enable; /* SPDIF input enable? */ @@ -850,6 +850,7 @@ struct hda_codec { unsigned int pin_amp_workaround:1; /* pin out-amp takes index * (e.g. Conexant codecs) */ + unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */ unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus); int snd_hda_resume(struct hda_bus *bus); #endif +#ifdef CONFIG_SND_HDA_POWER_SAVE +static inline +int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + if (codec->patch_ops.check_power_status) + return codec->patch_ops.check_power_status(codec, nid); + return 0; +} +#else +#define hda_call_check_power_status(codec, nid) 0 +#endif + /* * get widget information */ diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 803b298f7411..cb0c23a6b473 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -332,7 +332,6 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, AC_DIPSIZE_ELD_BUF); } -EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size); int snd_hdmi_get_eld(struct hdmi_eld *eld, struct hda_codec *codec, hda_nid_t nid) @@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, kfree(buf); return ret; } -EXPORT_SYMBOL_HDA(snd_hdmi_get_eld); static void hdmi_show_short_audio_desc(struct cea_sad *a) { @@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) } buf[j] = '\0'; /* necessary when j == 0 */ } -EXPORT_SYMBOL_HDA(snd_print_channel_allocation); void snd_hdmi_show_eld(struct hdmi_eld *e) { @@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e) for (i = 0; i < e->sad_count; i++) hdmi_show_short_audio_desc(e->sad + i); } -EXPORT_SYMBOL_HDA(snd_hdmi_show_eld); #ifdef CONFIG_PROC_FS @@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, return 0; } -EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) { @@ -594,7 +589,8 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) eld->proc_entry = NULL; } } -EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free); + +#endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, @@ -643,6 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max); pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps); } -EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info); - -#endif /* CONFIG_PROC_FS */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 5ea21285ee1f..fb0582f8d725 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -61,7 +61,6 @@ struct hda_gspec { struct hda_gnode *cap_vol_node; /* Node for capture volume */ unsigned int cur_cap_src; /* current capture source */ struct hda_input_mux input_mux; - char cap_labels[HDA_MAX_NUM_INPUTS][16]; unsigned int def_amp_in_caps; unsigned int def_amp_out_caps; @@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) * returns 0 if not found, 1 if found, or a negative error code. */ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node) + struct hda_gnode *node, int idx) { int i, err; unsigned int pinctl; - char *label; const char *type; if (node->checked) @@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, child = hda_get_node(spec, node->conn_list[i]); if (! child) continue; - err = parse_adc_sub_nodes(codec, spec, child); + err = parse_adc_sub_nodes(codec, spec, child, idx); if (err < 0) return err; if (err > 0) { @@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, return 0; type = "Input"; } - label = spec->cap_labels[spec->input_mux.num_items]; - strcpy(label, type); - spec->input_mux.items[spec->input_mux.num_items].label = label; + snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL); /* unmute the PIN external input */ unmute_input(codec, node, 0); /* index = 0? */ @@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, return 1; /* found */ } -/* add a capture source element */ -static void add_cap_src(struct hda_gspec *spec, int idx) -{ - struct hda_input_mux_item *csrc; - char *buf; - int num, ocap; - - num = spec->input_mux.num_items; - csrc = &spec->input_mux.items[num]; - buf = spec->cap_labels[num]; - for (ocap = 0; ocap < num; ocap++) { - if (! strcmp(buf, spec->cap_labels[ocap])) { - /* same label already exists, - * put the index number to be unique - */ - sprintf(buf, "%s %d", spec->cap_labels[ocap], num); - break; - } - } - csrc->index = idx; - spec->input_mux.num_items++; -} - /* * parse input */ @@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) for (i = 0; i < adc_node->nconns; i++) { node = hda_get_node(spec, adc_node->conn_list[i]); if (node && node->type == AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node); + err = parse_adc_sub_nodes(codec, spec, node, i); if (err < 0) return err; - else if (err > 0) - add_cap_src(spec, i); } } /* ... then check the rests, more complicated connections */ for (i = 0; i < adc_node->nconns; i++) { node = hda_get_node(spec, adc_node->conn_list[i]); if (node && node->type != AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node); + err = parse_adc_sub_nodes(codec, spec, node, i); if (err < 0) return err; - else if (err > 0) - add_cap_src(spec, i); } } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1053fff4bd0a..21aa9b0e28f6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); module_param_array(position_fix, int, NULL, 0444); -MODULE_PARM_DESC(position_fix, "Fix DMA pointer " - "(0 = auto, 1 = none, 2 = POSBUF)."); +MODULE_PARM_DESC(position_fix, "DMA pointer read method." + "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO)."); module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); @@ -126,6 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH10}," "{Intel, PCH}," "{Intel, CPT}," + "{Intel, PBG}," "{Intel, SCH}," "{ATI, SB450}," "{ATI, SB600}," @@ -304,6 +305,7 @@ enum { POS_FIX_AUTO, POS_FIX_LPIB, POS_FIX_POSBUF, + POS_FIX_VIACOMBO, }; /* Defines for ATI HD Audio support in SB450 south bridge */ @@ -432,7 +434,6 @@ struct azx { unsigned int polling_mode :1; unsigned int msi :1; unsigned int irq_pending_warned :1; - unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */ unsigned int probing :1; /* codec probing phase */ /* for debugging */ @@ -457,6 +458,7 @@ enum { AZX_DRIVER_ULI, AZX_DRIVER_NVIDIA, AZX_DRIVER_TERA, + AZX_DRIVER_CTX, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -472,6 +474,7 @@ static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ULI] = "HDA ULI M5461", [AZX_DRIVER_NVIDIA] = "HDA NVidia", [AZX_DRIVER_TERA] = "HDA Teradici", + [AZX_DRIVER_CTX] = "HDA Creative", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; @@ -562,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip) /* reset the rirb hw write pointer */ azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST); /* set N=1, get RIRB response interrupt for new entry */ - azx_writew(chip, RINTCNT, 1); + if (chip->driver_type == AZX_DRIVER_CTX) + azx_writew(chip, RINTCNT, 0xc0); + else + azx_writew(chip, RINTCNT, 1); /* enable rirb dma and response irq */ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); spin_unlock_irq(&chip->reg_lock); @@ -1135,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* clear rirb int */ status = azx_readb(chip, RIRBSTS); if (status & RIRB_INT_MASK) { - if (status & RIRB_INT_RESPONSE) + if (status & RIRB_INT_RESPONSE) { + if (chip->driver_type == AZX_DRIVER_CTX) + udelay(80); azx_update_rirb(chip); + } azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); } @@ -1308,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr)); /* enable the position buffer */ - if (chip->position_fix[0] == POS_FIX_POSBUF || - chip->position_fix[0] == POS_FIX_AUTO || - chip->position_fix[1] == POS_FIX_POSBUF || - chip->position_fix[1] == POS_FIX_AUTO || - chip->via_dmapos_patch) { + if (chip->position_fix[0] != POS_FIX_LPIB || + chip->position_fix[1] != POS_FIX_LPIB) { if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); @@ -1646,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int bufsize, period_bytes, format_val; + unsigned int bufsize, period_bytes, format_val, stream_tag; int err; azx_stream_reset(chip, azx_dev); @@ -1688,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) else azx_dev->fifo_size = 0; - return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag, + stream_tag = azx_dev->stream_tag; + /* CA-IBG chips need the playback stream starting from 1 */ + if (chip->driver_type == AZX_DRIVER_CTX && + stream_tag > chip->capture_streams) + stream_tag -= chip->capture_streams; + return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, azx_dev->format_val, substream); } @@ -1851,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev) { unsigned int pos; + int stream = azx_dev->substream->stream; - if (chip->via_dmapos_patch) + switch (chip->position_fix[stream]) { + case POS_FIX_LPIB: + /* read LPIB */ + pos = azx_sd_readl(azx_dev, SD_LPIB); + break; + case POS_FIX_VIACOMBO: pos = azx_via_get_position(chip, azx_dev); - else { - int stream = azx_dev->substream->stream; - if (chip->position_fix[stream] == POS_FIX_POSBUF || - chip->position_fix[stream] == POS_FIX_AUTO) { - /* use the position buffer */ - pos = le32_to_cpu(*azx_dev->posbuf); - } else { - /* read LPIB */ - pos = azx_sd_readl(azx_dev, SD_LPIB); - } + break; + default: + /* use the position buffer */ + pos = le32_to_cpu(*azx_dev->posbuf); } + if (pos >= azx_dev->bufsize) pos = 0; return pos; @@ -2312,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix) switch (fix) { case POS_FIX_LPIB: case POS_FIX_POSBUF: + case POS_FIX_VIACOMBO: return fix; } - /* Check VIA/ATI HD Audio Controller exist */ - switch (chip->driver_type) { - case AZX_DRIVER_VIA: - case AZX_DRIVER_ATI: - chip->via_dmapos_patch = 1; - /* Use link position directly, avoid any transfer problem. */ - return POS_FIX_LPIB; - } - chip->via_dmapos_patch = 0; - q = snd_pci_quirk_lookup(chip->pci, position_fix_list); if (q) { printk(KERN_INFO @@ -2333,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix) q->value, q->subvendor, q->subdevice); return q->value; } + + /* Check VIA/ATI HD Audio Controller exist */ + switch (chip->driver_type) { + case AZX_DRIVER_VIA: + case AZX_DRIVER_ATI: + /* Use link position directly, avoid any transfer problem. */ + return POS_FIX_VIACOMBO; + } + return POS_FIX_AUTO; } @@ -2734,23 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci) /* PCI IDs */ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { - /* ICH 6..10 */ - { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, - /* PCH */ - { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH }, - { PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH }, /* CPT */ { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH }, + /* PBG */ + { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH }, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, + /* Generic Intel */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_ICH }, /* ATI SB 450/600 */ { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI }, { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI }, @@ -2791,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_GENERIC }, + .driver_data = AZX_DRIVER_CTX }, #else /* this entry seems still valid -- i.e. without emu20kx chip */ - { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX }, #endif + /* Vortex86MX */ + { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 28ab4aead48f..46bbefe2e4a9 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -38,10 +38,11 @@ */ #define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) +#define HDA_AMP_VAL_MIN_MUTE (1<<29) #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) /* mono volume with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ +#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ .subdevice = HDA_SUBDEV_AMP_FLAG, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ @@ -51,16 +52,20 @@ .get = snd_hda_mixer_amp_volume_get, \ .put = snd_hda_mixer_amp_volume_put, \ .tlv = { .c = snd_hda_mixer_amp_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags } /* stereo volume with index */ #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) + HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0) /* mono volume */ #define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction) + HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0) /* stereo volume */ #define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) +/* stereo volume with min=mute */ +#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \ + HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \ + HDA_AMP_VAL_MIN_MUTE) /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ @@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); */ #define HDA_MAX_NUM_INPUTS 16 struct hda_input_mux_item { - const char *label; + char label[32]; unsigned int index; }; struct hda_input_mux { @@ -366,9 +371,7 @@ struct hda_bus_unsolicited { enum { AUTO_PIN_MIC, - AUTO_PIN_FRONT_MIC, - AUTO_PIN_LINE, - AUTO_PIN_FRONT_LINE, + AUTO_PIN_LINE_IN, AUTO_PIN_CD, AUTO_PIN_AUX, AUTO_PIN_LAST @@ -380,9 +383,33 @@ enum { AUTO_PIN_HP_OUT }; -extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST]; - #define AUTO_CFG_MAX_OUTS 5 +#define AUTO_CFG_MAX_INS 8 + +struct auto_pin_cfg_item { + hda_nid_t pin; + int type; +}; + +struct auto_pin_cfg; +const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin, + int check_location); +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input); +int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, + int index, int *type_index_ret); + +enum { + INPUT_PIN_ATTR_UNUSED, /* pin not connected */ + INPUT_PIN_ATTR_INT, /* internal mic/line-in */ + INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ + INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ +}; + +int snd_hda_get_input_pin_attr(unsigned int def_conf); struct auto_pin_cfg { int line_outs; @@ -393,7 +420,8 @@ struct auto_pin_cfg { int hp_outs; int line_out_type; /* AUTO_PIN_XXX_OUT */ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t input_pins[AUTO_PIN_LAST]; + int num_inputs; + struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; int dig_outs; hda_nid_t dig_out_pins[2]; hda_nid_t dig_in_pin; @@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) +#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) /* * CEA Short Audio Descriptor data diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index b697fd2a6f8b..f7ff3f7ccb8e 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.no_share_stream = 1; codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec) } codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, /* create input playback/capture controls for the given pin */ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, - const char *ctlname, int boost) + const char *ctlname, int ctlidx, int boost) { char name[32]; int err, idx; @@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, } /* create playback/capture controls for input pins */ -static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec, +static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { + struct ad198x_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux; - int i, err; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], - i <= AUTO_PIN_FRONT_MIC); + int i, err, type, type_idx; + + for (i = 0; i < cfg->num_inputs; i++) { + const char *label; + type = cfg->inputs[i].type; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, + ad1988_pin_to_adc_idx(cfg->inputs[i].pin), + &type_idx); + err = new_analog_input(spec, cfg->inputs[i].pin, + label, type_idx, + type == AUTO_PIN_MIC); if (err < 0) return err; - imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]); - imux->num_items++; } - imux->items[imux->num_items].label = "Mix"; - imux->items[imux->num_items].index = 9; - imux->num_items++; + snd_hda_add_imux_item(imux, "Mix", 9, NULL); if ((err = add_control(spec, AD_CTL_WIDGET_VOL, "Analog Mix Playback Volume", @@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec) static void ad1988_auto_init_analog_input(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int i, idx; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; - if (! nid) - continue; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; switch (nid) { case 0x15: /* port-C */ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); @@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) break; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN); + i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); @@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) "Speaker")) < 0 || (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], "Headphone")) < 0 || - (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) + (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) return err; spec->multiout.max_channels = spec->multiout.num_dacs * 2; @@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->vmaster_nid = 0x04; codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -3641,6 +3647,7 @@ static struct snd_pci_quirk ad1984_cfg_tbl[] = { /* Lenovo Thinkpad T61/X61 */ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD), SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), + SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP), {} }; @@ -4421,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec) } codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } @@ -4760,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec) } codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; return 0; } diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c deleted file mode 100644 index fb684f00156b..000000000000 --- a/sound/pci/hda/patch_atihdmi.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for ATI HDMI codecs - * - * Copyright (c) 2006 ATI Technologies Inc. - * - * - * This driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This driver 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/delay.h> -#include <linux/slab.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -struct atihdmi_spec { - struct hda_multi_out multiout; - - struct hda_pcm pcm_rec; -}; - -#define CVT_NID 0x02 /* audio converter */ -#define PIN_NID 0x03 /* HDMI output pin */ - -static struct hda_verb atihdmi_basic_init[] = { - /* enable digital output on pin widget */ - { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {} /* terminator */ -}; - -/* - * Controls - */ -static int atihdmi_build_controls(struct hda_codec *codec) -{ - struct atihdmi_spec *spec = codec->spec; - int err; - - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); - if (err < 0) - return err; - - return 0; -} - -static int atihdmi_init(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, atihdmi_basic_init); - /* SI codec requires to unmute the pin */ - if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, PIN_NID, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - return 0; -} - -/* - * Digital out - */ -static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct atihdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct atihdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct atihdmi_spec *spec = codec->spec; - int chans = substream->runtime->channels; - int i, err; - - err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); - if (err < 0) - return err; - snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT, - chans - 1); - /* FIXME: XXX */ - for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, CVT_NID, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (i << 4) | i); - } - return 0; -} - -static struct hda_pcm_stream atihdmi_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = CVT_NID, /* NID to query formats and rates and setup streams */ - .ops = { - .open = atihdmi_dig_playback_pcm_open, - .close = atihdmi_dig_playback_pcm_close, - .prepare = atihdmi_dig_playback_pcm_prepare - }, -}; - -static int atihdmi_build_pcms(struct hda_codec *codec) -{ - struct atihdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; - unsigned int chans; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "ATI HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; - - /* FIXME: we must check ELD and change the PCM parameters dynamically - */ - chans = get_wcaps(codec, CVT_NID); - chans = get_wcaps_channels(chans); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; - - return 0; -} - -static void atihdmi_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - -static struct hda_codec_ops atihdmi_patch_ops = { - .build_controls = atihdmi_build_controls, - .build_pcms = atihdmi_build_pcms, - .init = atihdmi_init, - .free = atihdmi_free, -}; - -static int patch_atihdmi(struct hda_codec *codec) -{ - struct atihdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - /* NID for copying analog to digital, - * seems to be unused in pure-digital - * case. - */ - spec->multiout.dig_out_nid = CVT_NID; - - codec->patch_ops = atihdmi_patch_ops; - - return 0; -} - -/* - * patch entries - */ -static struct hda_codec_preset snd_hda_preset_atihdmi[] = { - { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, - { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, - { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi }, - { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:1002793c"); -MODULE_ALIAS("snd-hda-codec-id:10027919"); -MODULE_ALIAS("snd-hda-codec-id:1002791a"); -MODULE_ALIAS("snd-hda-codec-id:1002aa01"); -MODULE_ALIAS("snd-hda-codec-id:10951390"); -MODULE_ALIAS("snd-hda-codec-id:17e80047"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ATI HDMI HD-audio codec"); - -static struct hda_codec_preset_list atihdmi_list = { - .preset = snd_hda_preset_atihdmi, - .owner = THIS_MODULE, -}; - -static int __init patch_atihdmi_init(void) -{ - return snd_hda_add_codec_preset(&atihdmi_list); -} - -static void __exit patch_atihdmi_exit(void) -{ - snd_hda_delete_codec_preset(&atihdmi_list); -} - -module_init(patch_atihdmi_init) -module_exit(patch_atihdmi_exit) diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index af478019088e..46c8bf48c31f 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec) spec->dig_in = nid; continue; } - for (j = 0; j < AUTO_PIN_LAST; j++) - if (cfg->input_pins[j] == pin) + for (j = 0; j < cfg->num_inputs; j++) + if (cfg->inputs[j].pin == pin) break; - if (j >= AUTO_PIN_LAST) + if (j >= cfg->num_inputs) continue; spec->input_pins[n] = pin; - spec->input_labels[n] = auto_pin_cfg_labels[j]; + spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1); spec->adcs[n] = nid; n++; } @@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec) if (cfg->dig_outs && snd_hda_get_connections(codec, cfg->dig_out_pins[0], &spec->dig_out, 1) == 1) - spec->multiout.dig_out_nid = cfg->dig_out_pins[0]; + spec->multiout.dig_out_nid = spec->dig_out; } static int ca0110_parse_auto_config(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 4ef5efaaaef1..460fb2ef7e39 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -65,6 +65,7 @@ struct cs_spec { /* available models */ enum { + CS420X_MBP53, CS420X_MBP55, CS420X_IMAC27, CS420X_AUTO, @@ -329,12 +330,12 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t pin = cfg->input_pins[idx]; + hda_nid_t pin = cfg->inputs[idx].pin; unsigned int val = snd_hda_query_pin_caps(codec, pin); if (!(val & AC_PINCAP_PRES_DETECT)) return 0; val = snd_hda_codec_get_pincfg(codec, pin); - return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX); + return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT); } static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, @@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t pin = cfg->input_pins[i]; - if (!pin) - continue; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; spec->input_idx[spec->num_inputs] = i; spec->capsrc_idx[i] = spec->num_inputs++; spec->cur_input = i; @@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec) /* check whether the automatic mic switch is available */ if (spec->num_inputs == 2 && - spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) { - if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) { - if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + cfg->inputs[0].type == AUTO_PIN_MIC && + cfg->inputs[1].type == AUTO_PIN_MIC) { + if (is_ext_mic(codec, cfg->inputs[0].pin)) { + if (!is_ext_mic(codec, cfg->inputs[1].pin)) { spec->mic_detect = 1; - spec->automic_idx = AUTO_PIN_FRONT_MIC; + spec->automic_idx = 0; } } else { - if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + if (is_ext_mic(codec, cfg->inputs[1].pin)) { spec->mic_detect = 1; - spec->automic_idx = AUTO_PIN_MIC; + spec->automic_idx = 1; } } } @@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= spec->num_inputs) uinfo->value.enumerated.item = spec->num_inputs - 1; idx = spec->input_idx[uinfo->value.enumerated.item]; - strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]); + strcpy(uinfo->value.enumerated.name, + hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1)); return 0; } @@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, return bind; } +/* add a (input-boost) volume control to the given input pin */ +static int add_input_volume_control(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + int item) +{ + hda_nid_t pin = cfg->inputs[item].pin; + u32 caps; + const char *label; + struct snd_kcontrol *kctl; + + if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) + return 0; + caps = query_amp_caps(codec, pin, HDA_INPUT); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (caps <= 1) + return 0; + label = hda_get_autocfg_input_label(codec, cfg, item); + return add_volume(codec, label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + static int build_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; @@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec) return err; } + for (i = 0; i < spec->num_inputs; i++) { + err = add_input_volume_control(codec, &spec->autocfg, i); + if (err < 0) + return err; + } + return 0; } @@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, hp_present ? 0 : PIN_OUT); } - if (spec->board_config == CS420X_MBP55 || + if (spec->board_config == CS420X_MBP53 || + spec->board_config == CS420X_MBP55 || spec->board_config == CS420X_IMAC27) { unsigned int gpio = hp_present ? 0x02 : 0x08; snd_hda_codec_write(codec, 0x01, 0, @@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec) hda_nid_t nid; unsigned int present; - nid = cfg->input_pins[spec->automic_idx]; + nid = cfg->inputs[spec->automic_idx].pin; present = snd_hda_jack_detect(codec, nid); if (present) change_cur_input(codec, spec->automic_idx, 0); - else { - unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? - AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC; - change_cur_input(codec, imic, 0); - } + else + change_cur_input(codec, !spec->automic_idx, 0); } /* @@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec) unsigned int coef; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { + for (i = 0; i < cfg->num_inputs; i++) { unsigned int ctl; - hda_nid_t pin = cfg->input_pins[i]; - if (!pin || !spec->adc_nid[i]) + hda_nid_t pin = cfg->inputs[i].pin; + if (!spec->adc_nid[i]) continue; /* set appropriate pin control and mute first */ ctl = PIN_IN; - if (i <= AUTO_PIN_FRONT_MIC) { + if (cfg->inputs[i].type == AUTO_PIN_MIC) { unsigned int caps = snd_hda_query_pin_caps(codec, pin); caps >>= AC_PINCAP_VREF_SHIFT; if (caps & AC_PINCAP_VREF_80) @@ -972,6 +999,53 @@ static struct hda_verb cs_coef_init_verbs[] = { {} /* terminator */ }; +/* Errata: CS4207 rev C0/C1/C2 Silicon + * + * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf + * + * 6. At high temperature (TA > +85°C), the digital supply current (IVD) + * may be excessive (up to an additional 200 μA), which is most easily + * observed while the part is being held in reset (RESET# active low). + * + * Root Cause: At initial powerup of the device, the logic that drives + * the clock and write enable to the S/PDIF SRC RAMs is not properly + * initialized. + * Certain random patterns will cause a steady leakage current in those + * RAM cells. The issue will resolve once the SRCs are used (turned on). + * + * Workaround: The following verb sequence briefly turns on the S/PDIF SRC + * blocks, which will alleviate the issue. + */ + +static struct hda_verb cs_errata_init_verbs[] = { + {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ + {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + + {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, + {0x11, AC_VERB_SET_PROC_COEF, 0x9999}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, + {0x11, AC_VERB_SET_PROC_COEF, 0xa412}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, + {0x11, AC_VERB_SET_PROC_COEF, 0x0009}, + + {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */ + {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */ + + {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, + {0x11, AC_VERB_SET_PROC_COEF, 0x2412}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, + {0x11, AC_VERB_SET_PROC_COEF, 0x0000}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, + {0x11, AC_VERB_SET_PROC_COEF, 0x0008}, + {0x11, AC_VERB_SET_PROC_STATE, 0x00}, + + {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */ + {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */ + /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */ + + {} /* terminator */ +}; + /* SPDIF setup */ static void init_digital(struct hda_codec *codec) { @@ -991,6 +1065,9 @@ static int cs_init(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; + /* init_verb sequence for C0/C1/C2 errata*/ + snd_hda_sequence_write(codec, cs_errata_init_verbs); + snd_hda_sequence_write(codec, cs_coef_init_verbs); if (spec->gpio_mask) { @@ -1080,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec) } static const char *cs420x_models[CS420X_MODELS] = { + [CS420X_MBP53] = "mbp53", [CS420X_MBP55] = "mbp55", [CS420X_IMAC27] = "imac27", [CS420X_AUTO] = "auto", @@ -1087,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = { static struct snd_pci_quirk cs420x_cfg_tbl[] = { + SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), + SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27), {} /* terminator */ }; @@ -1097,6 +1177,20 @@ struct cs_pincfg { u32 val; }; +static struct cs_pincfg mbp53_pincfgs[] = { + { 0x09, 0x012b4050 }, + { 0x0a, 0x90100141 }, + { 0x0b, 0x90100140 }, + { 0x0c, 0x018b3020 }, + { 0x0d, 0x90a00110 }, + { 0x0e, 0x400000f0 }, + { 0x0f, 0x01cbe030 }, + { 0x10, 0x014be060 }, + { 0x12, 0x400000f0 }, + { 0x15, 0x400000f0 }, + {} /* terminator */ +}; + static struct cs_pincfg mbp55_pincfgs[] = { { 0x09, 0x012b4030 }, { 0x0a, 0x90100121 }, @@ -1126,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = { }; static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { + [CS420X_MBP53] = mbp53_pincfgs, [CS420X_MBP55] = mbp55_pincfgs, [CS420X_IMAC27] = imac27_pincfgs, }; @@ -1158,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec) switch (spec->board_config) { case CS420X_IMAC27: + case CS420X_MBP53: case CS420X_MBP55: /* GPIO1 = headphones */ /* GPIO3 = speakers */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 31b5d9eeba68..6361f752b5f3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -57,6 +57,12 @@ struct conexant_jack { }; +struct pin_dac_pair { + hda_nid_t pin; + hda_nid_t dac; + int type; +}; + struct conexant_spec { struct snd_kcontrol_new *mixers[5]; @@ -77,6 +83,7 @@ struct conexant_spec { unsigned int cur_eapd; unsigned int hp_present; unsigned int auto_mic; + int auto_mic_ext; /* autocfg.inputs[] index for ext mic */ unsigned int need_dac_fix; /* capture */ @@ -110,12 +117,16 @@ struct conexant_spec { struct auto_pin_cfg autocfg; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + struct pin_dac_pair dac_info[8]; + int dac_info_filled; - unsigned int dell_automute; unsigned int port_d_mode; + unsigned int auto_mute:1; /* used in auto-parser */ + unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; + unsigned int hp_laptop:1; unsigned int ext_mic_present; unsigned int recording; @@ -2299,6 +2310,18 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec) } } +/* toggle input of built-in digital mic and mic jack appropriately */ +static void cxt5066_hp_laptop_automic(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x1b); + snd_printdd("CXT5066: external microphone present=%d\n", present); + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, + present ? 1 : 3); +} + + /* toggle input of built-in digital mic and mic jack appropriately order is: external mic -> dock mic -> interal mic */ static void cxt5066_thinkpad_automic(struct hda_codec *codec) @@ -2408,6 +2431,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res) } /* unsolicited event for jack sensing */ +static void cxt5066_hp_laptop_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066_hp_laptop: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_hp_laptop_automic(codec); + break; + } +} + +/* unsolicited event for jack sensing */ static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res) { snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26); @@ -2989,6 +3026,14 @@ static struct hda_verb cxt5066_init_verbs_portd_lo[] = { { } /* end */ }; + +static struct hda_verb cxt5066_init_verbs_hp_laptop[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + /* initialize jack-sensing, too */ static int cxt5066_init(struct hda_codec *codec) { @@ -3004,6 +3049,8 @@ static int cxt5066_init(struct hda_codec *codec) cxt5066_ideapad_automic(codec); else if (spec->thinkpad) cxt5066_thinkpad_automic(codec); + else if (spec->hp_laptop) + cxt5066_hp_laptop_automic(codec); } cxt5066_set_mic_boost(codec); return 0; @@ -3028,9 +3075,10 @@ enum { CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ - CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ + CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ + CXT5066_HP_LAPTOP, /* HP Laptop */ CXT5066_MODELS }; @@ -3038,26 +3086,33 @@ static const char *cxt5066_models[CXT5066_MODELS] = { [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", - [CXT5066_DELL_VOSTO] = "dell-vostro", + [CXT5066_DELL_VOSTRO] = "dell-vostro", [CXT5066_IDEAPAD] = "ideapad", [CXT5066_THINKPAD] = "thinkpad", + [CXT5066_HP_LAPTOP] = "hp-laptop", }; static struct snd_pci_quirk cxt5066_cfg_tbl[] = { - SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", - CXT5066_LAPTOP), + SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", CXT5066_DELL_LAPTOP), - SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), - SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), + SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", + CXT5066_LAPTOP), + SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x390a, "Lenovo S10-3t", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} @@ -3114,6 +3169,23 @@ static int patch_cxt5066(struct hda_codec *codec) spec->num_init_verbs++; spec->dell_automute = 1; break; + case CXT5066_HP_LAPTOP: + codec->patch_ops.init = cxt5066_init; + codec->patch_ops.unsol_event = cxt5066_hp_laptop_event; + spec->init_verbs[spec->num_init_verbs] = + cxt5066_init_verbs_hp_laptop; + spec->num_init_verbs++; + spec->hp_laptop = 1; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ + spec->input_mux = NULL; + spec->port_d_mode = 0; + spec->mic_boost = 3; /* default 30dB gain */ + break; + case CXT5066_OLPC_XO_1_5: codec->patch_ops.init = cxt5066_olpc_init; codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; @@ -3135,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec) spec->capture_prepare = cxt5066_olpc_capture_prepare; spec->capture_cleanup = cxt5066_olpc_capture_cleanup; break; - case CXT5066_DELL_VOSTO: + case CXT5066_DELL_VOSTRO: codec->patch_ops.init = cxt5066_init; codec->patch_ops.unsol_event = cxt5066_vostro_event; spec->init_verbs[0] = cxt5066_init_verbs_vostro; @@ -3193,6 +3265,604 @@ static int patch_cxt5066(struct hda_codec *codec) } /* + * Automatic parser for CX20641 & co + */ + +static hda_nid_t cx_auto_adc_nids[] = { 0x14 }; + +/* get the connection index of @nid in the widget @mux */ +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + +/* get an unassigned DAC from the given list. + * Return the nid if found and reduce the DAC list, or return zero if + * not found + */ +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t *dacs, int *num_dacs) +{ + int i, nums = *num_dacs; + hda_nid_t ret = 0; + + for (i = 0; i < nums; i++) { + if (get_connection_index(codec, pin, dacs[i]) >= 0) { + ret = dacs[i]; + break; + } + } + if (!ret) + return 0; + if (--nums > 0) + memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); + *num_dacs = nums; + return ret; +} + +#define MAX_AUTO_DACS 5 + +/* fill analog DAC list from the widget tree */ +static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) +{ + hda_nid_t nid, end_nid; + int nums = 0; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { + dacs[nums++] = nid; + if (nums >= MAX_AUTO_DACS) + break; + } + } + return nums; +} + +/* fill pin_dac_pair list from the pin and dac list */ +static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, + int num_pins, hda_nid_t *dacs, int *rest, + struct pin_dac_pair *filled, int type) +{ + int i, nums; + + nums = 0; + for (i = 0; i < num_pins; i++) { + filled[nums].pin = pins[i]; + filled[nums].type = type; + filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); + nums++; + } + return nums; +} + +/* parse analog output paths */ +static void cx_auto_parse_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t dacs[MAX_AUTO_DACS]; + int i, j, nums, rest; + + rest = fill_cx_auto_dacs(codec, dacs); + /* parse all analog output pins */ + nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, + dacs, &rest, spec->dac_info, + AUTO_PIN_LINE_OUT); + nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, + dacs, &rest, spec->dac_info + nums, + AUTO_PIN_HP_OUT); + nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, + dacs, &rest, spec->dac_info + nums, + AUTO_PIN_SPEAKER_OUT); + spec->dac_info_filled = nums; + /* fill multiout struct */ + for (i = 0; i < nums; i++) { + hda_nid_t dac = spec->dac_info[i].dac; + if (!dac) + continue; + switch (spec->dac_info[i].type) { + case AUTO_PIN_LINE_OUT: + spec->private_dac_nids[spec->multiout.num_dacs] = dac; + spec->multiout.num_dacs++; + break; + case AUTO_PIN_HP_OUT: + case AUTO_PIN_SPEAKER_OUT: + if (!spec->multiout.hp_nid) { + spec->multiout.hp_nid = dac; + break; + } + for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) + if (!spec->multiout.extra_out_nid[j]) { + spec->multiout.extra_out_nid[j] = dac; + break; + } + break; + } + } + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.max_channels = nums * 2; + + if (cfg->hp_outs > 0) + spec->auto_mute = 1; + spec->vmaster_nid = spec->private_dac_nids[0]; +} + +/* auto-mute/unmute speaker and line outs according to headphone jack */ +static void cx_auto_hp_automute(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, present; + + if (!spec->auto_mute) + return; + present = 0; + for (i = 0; i < cfg->hp_outs; i++) { + if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) { + present = 1; + break; + } + } + for (i = 0; i < cfg->line_outs; i++) { + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); + } + for (i = 0; i < cfg->speaker_outs; i++) { + snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); + } +} + +/* automatic switch internal and external mic */ +static void cx_auto_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->private_imux; + int ext_idx = spec->auto_mic_ext; + + if (!spec->auto_mic) + return; + if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) { + snd_hda_codec_write(codec, spec->adc_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[ext_idx].index); + } else { + snd_hda_codec_write(codec, spec->adc_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[!ext_idx].index); + } +} + +static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) +{ + int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cx_auto_hp_automute(codec); + conexant_report_jack(codec, nid); + break; + case CONEXANT_MIC_EVENT: + cx_auto_automic(codec); + conexant_report_jack(codec, nid); + break; + } +} + +/* return true if it's an internal-mic pin */ +static int is_int_mic(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT; +} + +/* return true if it's an external-mic pin */ +static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL && + (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT); +} + +/* check whether the pin config is suitable for auto-mic switching; + * auto-mic is enabled only when one int-mic and one-ext mic exist + */ +static void cx_auto_check_auto_mic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (is_ext_mic(codec, cfg->inputs[0].pin) && + is_int_mic(codec, cfg->inputs[1].pin)) { + spec->auto_mic = 1; + spec->auto_mic_ext = 1; + return; + } + if (is_int_mic(codec, cfg->inputs[1].pin) && + is_ext_mic(codec, cfg->inputs[0].pin)) { + spec->auto_mic = 1; + spec->auto_mic_ext = 0; + return; + } +} + +static void cx_auto_parse_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux; + int i; + + imux = &spec->private_imux; + for (i = 0; i < cfg->num_inputs; i++) { + int idx = get_connection_index(codec, spec->adc_nids[0], + cfg->inputs[i].pin); + if (idx >= 0) { + const char *label; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, idx, NULL); + } + } + if (imux->num_items == 2 && cfg->num_inputs == 2) + cx_auto_check_auto_mic(codec); + if (imux->num_items > 1 && !spec->auto_mic) + spec->input_mux = imux; +} + +/* get digital-input audio widget corresponding to the given pin */ +static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { + if (get_connection_index(codec, nid, pin) >= 0) + return nid; + } + } + return 0; +} + +static void cx_auto_parse_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) + spec->multiout.dig_out_nid = nid; + if (cfg->dig_in_pin) + spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); +} + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static void cx_auto_parse_beep(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define cx_auto_parse_beep(codec) +#endif + +static int cx_auto_parse_auto_config(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + cx_auto_parse_output(codec); + cx_auto_parse_input(codec); + cx_auto_parse_digital(codec); + cx_auto_parse_beep(codec); + return 0; +} + +static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins, + hda_nid_t *pins) +{ + int i; + for (i = 0; i < num_pins; i++) { + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } +} + +static void select_connection(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t src) +{ + int idx = get_connection_index(codec, pin, src); + if (idx >= 0) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, idx); +} + +static void cx_auto_init_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) + snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + for (i = 0; i < cfg->hp_outs; i++) + snd_hda_codec_write(codec, cfg->hp_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (spec->auto_mute) { + for (i = 0; i < cfg->hp_outs; i++) { + snd_hda_codec_write(codec, cfg->hp_pins[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | CONEXANT_HP_EVENT); + } + cx_auto_hp_automute(codec); + } else { + for (i = 0; i < cfg->line_outs; i++) + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + for (i = 0; i < cfg->speaker_outs; i++) + snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + } + + for (i = 0; i < spec->dac_info_filled; i++) { + nid = spec->dac_info[i].dac; + if (!nid) + nid = spec->multiout.dac_nids[0]; + select_connection(codec, spec->dac_info[i].pin, nid); + } + + /* turn on EAPD */ + cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins); + cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins); + cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins); +} + +static void cx_auto_init_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)); + + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int type; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + type = PIN_VREF80; + else + type = PIN_IN; + snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, type); + } + + if (spec->auto_mic) { + int ext_idx = spec->auto_mic_ext; + snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | CONEXANT_MIC_EVENT); + cx_auto_automic(codec); + } else { + for (i = 0; i < spec->num_adc_nids; i++) { + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + spec->private_imux.items[0].index); + } + } +} + +static void cx_auto_init_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (spec->multiout.dig_out_nid) + snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (spec->dig_in_nid) + snd_hda_codec_write(codec, cfg->dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); +} + +static int cx_auto_init(struct hda_codec *codec) +{ + /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ + cx_auto_init_output(codec); + cx_auto_init_input(codec); + cx_auto_init_digital(codec); + return 0; +} + +static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, + const char *dir, int cidx, + hda_nid_t nid, int hda_dir) +{ + static char name[32]; + static struct snd_kcontrol_new knew[] = { + HDA_CODEC_VOLUME(name, 0, 0, 0), + HDA_CODEC_MUTE(name, 0, 0, 0), + }; + static char *sfx[2] = { "Volume", "Switch" }; + int i, err; + + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir); + knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; + knew[i].index = cidx; + snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); + kctl = snd_ctl_new1(&knew[i], codec); + if (!kctl) + return -ENOMEM; + err = snd_hda_ctl_add(codec, nid, kctl); + if (err < 0) + return err; + if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE)) + break; + } + return 0; +} + +#define cx_auto_add_pb_volume(codec, nid, str, idx) \ + cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) + +static int cx_auto_build_output_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i, err; + int num_line = 0, num_hp = 0, num_spk = 0; + static const char *texts[3] = { "Front", "Surround", "CLFE" }; + + if (spec->dac_info_filled == 1) + return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac, + "Master", 0); + for (i = 0; i < spec->dac_info_filled; i++) { + const char *label; + int idx, type; + if (!spec->dac_info[i].dac) + continue; + type = spec->dac_info[i].type; + if (type == AUTO_PIN_LINE_OUT) + type = spec->autocfg.line_out_type; + switch (type) { + case AUTO_PIN_LINE_OUT: + default: + label = texts[num_line++]; + idx = 0; + break; + case AUTO_PIN_HP_OUT: + label = "Headphone"; + idx = num_hp++; + break; + case AUTO_PIN_SPEAKER_OUT: + label = "Speaker"; + idx = num_spk++; + break; + } + err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac, + label, idx); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_build_input_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + static const char *prev_label; + int i, err, cidx; + + err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0], + HDA_INPUT); + if (err < 0) + return err; + prev_label = NULL; + cidx = 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + const char *label; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + if (label == prev_label) + cidx++; + else + cidx = 0; + prev_label = label; + err = cx_auto_add_volume(codec, label, " Capture", cidx, + nid, HDA_INPUT); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_build_controls(struct hda_codec *codec) +{ + int err; + + err = cx_auto_build_output_controls(codec); + if (err < 0) + return err; + err = cx_auto_build_input_controls(codec); + if (err < 0) + return err; + return conexant_build_controls(codec); +} + +static struct hda_codec_ops cx_auto_patch_ops = { + .build_controls = cx_auto_build_controls, + .build_pcms = conexant_build_pcms, + .init = cx_auto_init, + .free = conexant_free, + .unsol_event = cx_auto_unsol_event, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .suspend = conexant_suspend, +#endif + .reboot_notify = snd_hda_shutup_pins, +}; + +static int patch_conexant_auto(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + spec->adc_nids = cx_auto_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids); + spec->capsrc_nids = spec->adc_nids; + err = cx_auto_parse_auto_config(codec); + if (err < 0) { + kfree(codec->spec); + codec->spec = NULL; + return err; + } + codec->patch_ops = cx_auto_patch_ops; + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; +} + +/* */ static struct hda_codec_preset snd_hda_preset_conexant[] = { @@ -3210,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5066 }, { .id = 0x14f15069, .name = "CX20585", .patch = patch_cxt5066 }, + { .id = 0x14f15097, .name = "CX20631", + .patch = patch_conexant_auto }, + { .id = 0x14f15098, .name = "CX20632", + .patch = patch_conexant_auto }, + { .id = 0x14f150a1, .name = "CX20641", + .patch = patch_conexant_auto }, + { .id = 0x14f150a2, .name = "CX20642", + .patch = patch_conexant_auto }, + { .id = 0x14f150ab, .name = "CX20651", + .patch = patch_conexant_auto }, + { .id = 0x14f150ac, .name = "CX20652", + .patch = patch_conexant_auto }, + { .id = 0x14f150b8, .name = "CX20664", + .patch = patch_conexant_auto }, + { .id = 0x14f150b9, .name = "CX20665", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -3220,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15068"); MODULE_ALIAS("snd-hda-codec-id:14f15069"); +MODULE_ALIAS("snd-hda-codec-id:14f15097"); +MODULE_ALIAS("snd-hda-codec-id:14f15098"); +MODULE_ALIAS("snd-hda-codec-id:14f150a1"); +MODULE_ALIAS("snd-hda-codec-id:14f150a2"); +MODULE_ALIAS("snd-hda-codec-id:14f150ab"); +MODULE_ALIAS("snd-hda-codec-id:14f150ac"); +MODULE_ALIAS("snd-hda-codec-id:14f150b8"); +MODULE_ALIAS("snd-hda-codec-id:14f150b9"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2bc0f07cf33f..d3e49aa5b9ec 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3,6 +3,9 @@ * patch_hdmi.c - routines for HDMI/DisplayPort codecs * * Copyright(c) 2008-2010 Intel Corporation. All rights reserved. + * Copyright (c) 2006 ATI Technologies Inc. + * Copyright (c) 2008 NVIDIA Corp. All rights reserved. + * Copyright (c) 2008 Wei Ni <wni@nvidia.com> * * Authors: * Wu Fengguang <wfg@linux.intel.com> @@ -25,6 +28,22 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +/* + * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device + * could support two independent pipes, each of them can be connected to one or + * more ports (DVI, HDMI or DisplayPort). + * + * The HDA correspondence of pipes/ports are converter/pin nodes. + */ +#define MAX_HDMI_CVTS 3 +#define MAX_HDMI_PINS 3 struct hdmi_spec { int num_cvts; @@ -49,10 +68,10 @@ struct hdmi_spec { struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS]; /* - * nvhdmi specific + * ati/nvhdmi specific */ struct hda_multi_out multiout; - unsigned int codec_type; + struct hda_pcm_stream *pcm_playback; /* misc flags */ /* PD bit indicates only the update, not the current state */ @@ -65,13 +84,25 @@ struct hdmi_audio_infoframe { u8 ver; /* 0x01 */ u8 len; /* 0x0a */ - u8 checksum; /* PB0 */ + u8 checksum; + u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ u8 SS01_SF24; u8 CXT04; u8 CA; u8 LFEPBL01_LSV36_DM_INH7; - u8 reserved[5]; /* PB6 - PB10 */ +}; + +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; }; /* @@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = { /* 4ch */ [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, + [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, /* surround50 */ [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, /* surround51 */ @@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = { * This is an ordered list! * * The preceding ones have better chances to be selected by - * hdmi_setup_channel_allocation(). + * hdmi_channel_allocation(). */ static struct cea_channel_speaker_allocation channel_allocations[] = { /* channel: 7 6 5 4 3 2 1 0 */ @@ -352,14 +383,14 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, - struct hdmi_audio_infoframe *ai) +static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, + int channels) { struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld; int i; + int ca = 0; int spk_mask = 0; - int channels = 1 + (ai->CC02_CT47 & 0x7); char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; /* @@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, if (channels == channel_allocations[i].channels && (spk_mask & channel_allocations[i].spk_mask) == channel_allocations[i].spk_mask) { - ai->CA = channel_allocations[i].ca_index; + ca = channel_allocations[i].ca_index; break; } } snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ai->CA, channels, buf); + ca, channels, buf); - return ai->CA; + return ca; } static void hdmi_debug_channel_mapping(struct hda_codec *codec, @@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) + int ca) { int i; - int ca = ai->CA; int err; if (hdmi_channel_mapping[ca][1] == 0) { @@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) #endif } -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) { - u8 *bytes = (u8 *)ai; + u8 *bytes = (u8 *)hdmi_ai; u8 sum = 0; int i; - ai->checksum = 0; + hdmi_ai->checksum = 0; - for (i = 0; i < sizeof(*ai); i++) + for (i = 0; i < sizeof(*hdmi_ai); i++) sum += bytes[i]; - ai->checksum = -sum; + hdmi_ai->checksum = -sum; } static void hdmi_fill_audio_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) + u8 *dip, int size) { - u8 *bytes = (u8 *)ai; int i; hdmi_debug_dip_size(codec, pin_nid); hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - hdmi_checksum_audio_infoframe(ai); - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) - hdmi_write_dip_byte(codec, pin_nid, bytes[i]); + for (i = 0; i < size; i++) + hdmi_write_dip_byte(codec, pin_nid, dip[i]); } static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) + u8 *dip, int size) { - u8 *bytes = (u8 *)ai; u8 val; int i; @@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return false; hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) { + for (i = 0; i < size; i++) { val = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != bytes[i]) + if (val != dip[i]) return false; } @@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, { struct hdmi_spec *spec = codec->spec; hda_nid_t pin_nid; + int channels = substream->runtime->channels; + int ca; int i; - struct hdmi_audio_infoframe ai = { - .type = 0x84, - .ver = 0x01, - .len = 0x0a, - .CC02_CT47 = substream->runtime->channels - 1, - }; + u8 ai[max(sizeof(struct hdmi_audio_infoframe), + sizeof(struct dp_audio_infoframe))]; - hdmi_setup_channel_allocation(codec, nid, &ai); + ca = hdmi_channel_allocation(codec, nid, channels); for (i = 0; i < spec->num_pins; i++) { if (spec->pin_cvt[i] != nid) @@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, continue; pin_nid = spec->pin[i]; - if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + + memset(ai, 0, sizeof(ai)); + if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ + struct hdmi_audio_infoframe *hdmi_ai; + + hdmi_ai = (struct hdmi_audio_infoframe *)ai; + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + hdmi_ai->CC02_CT47 = channels - 1; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai; + + dp_ai = (struct dp_audio_infoframe *)ai; + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = channels - 1; + } else { + snd_printd("HDMI: unknown connection type at pin %d\n", + pin_nid); + continue; + } + + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) { snd_printdd("hdmi_setup_audio_infoframe: " "cvt=%d pin=%d channels=%d\n", nid, pin_nid, - substream->runtime->channels); - hdmi_setup_channel_mapping(codec, pin_nid, &ai); + channels); + hdmi_setup_channel_mapping(codec, pin_nid, ca); hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai, sizeof(ai)); hdmi_start_infoframe_trans(codec, pin_nid); } } @@ -707,8 +762,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int format) { struct hdmi_spec *spec = codec->spec; - int tag; - int fmt; int pinctl; int new_pinctl = 0; int i; @@ -745,24 +798,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, return -EINVAL; } - tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; - fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); - - snd_printdd("hdmi_setup_stream: " - "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", - nid, - tag == stream_tag ? "" : "new-", - stream_tag, - fmt == format ? "" : "new-", - format); - - if (tag != stream_tag) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - stream_tag << 4); - if (fmt != format) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, format); + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); return 0; } @@ -810,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* * HDA/HDMI auto parsing */ - static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; @@ -941,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec) return 0; } +/* + */ +static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = { + "HDMI 0", + "HDMI 1", + "HDMI 2", +}; + +/* + * HDMI callbacks + */ + +static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + hdmi_set_channel_count(codec, hinfo->nid, + substream->runtime->channels); + + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); + + return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); +} + +static struct hda_pcm_stream generic_hdmi_pcm_playback = { + .substreams = 1, + .channels_min = 2, + .ops = { + .open = hdmi_pcm_open, + .prepare = generic_hdmi_playback_pcm_prepare, + }, +}; + +static int generic_hdmi_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + int i; + + codec->num_pcms = spec->num_cvts; + codec->pcm_info = info; + + for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + struct hda_pcm_stream *pstr; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + + info->name = generic_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + if (spec->pcm_playback) + *pstr = *spec->pcm_playback; + else + *pstr = generic_hdmi_pcm_playback; + pstr->nid = spec->cvt[i]; + if (pstr->channels_max <= 2 && chans && chans <= 16) + pstr->channels_max = chans; + } + + return 0; +} + +static int generic_hdmi_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err; + int i; + + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + if (err < 0) + return err; + } + + return 0; +} + +static int generic_hdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; spec->pin[i]; i++) { + hdmi_enable_output(codec, spec->pin[i]); + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | spec->pin[i]); + } + return 0; +} + +static void generic_hdmi_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); + + kfree(spec); +} + +static struct hda_codec_ops generic_hdmi_patch_ops = { + .init = generic_hdmi_init, + .free = generic_hdmi_free, + .build_pcms = generic_hdmi_build_pcms, + .build_controls = generic_hdmi_build_controls, + .unsol_event = hdmi_unsol_event, +}; + +static int patch_generic_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int i; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + if (hdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } + codec->patch_ops = generic_hdmi_patch_ops; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); + + init_channel_allocations(); + + return 0; +} + +/* + * Nvidia specific implementations + */ + +#define Nv_VERB_SET_Channel_Allocation 0xF79 +#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A +#define Nv_VERB_SET_Audio_Protection_On 0xF98 +#define Nv_VERB_SET_Audio_Protection_Off 0xF99 + +#define nvhdmi_master_con_nid_7x 0x04 +#define nvhdmi_master_pin_nid_7x 0x05 + +static hda_nid_t nvhdmi_con_nids_7x[4] = { + /*front, rear, clfe, rear_surr */ + 0x6, 0x8, 0xa, 0xc, +}; + +static struct hda_verb nvhdmi_basic_init_7x[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, + /* enable digital output on pin widget */ + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + {} /* terminator */ +}; + +#ifdef LIMITED_RATE_FMT_SUPPORT +/* support only the safe format and rate */ +#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 +#define SUPPORTED_MAXBPS 16 +#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#else +/* support all rates and formats */ +#define SUPPORTED_RATES \ + (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define SUPPORTED_MAXBPS 24 +#define SUPPORTED_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#endif + +static int nvhdmi_7x_init(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x); + return 0; +} + +static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int i; + + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, + 0, AC_VERB_SET_CHANNEL_STREAMID, 0); + for (i = 0; i < 4; i++) { + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_STREAM_FORMAT, 0); + } + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int chs; + unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; + int i; + + mutex_lock(&codec->spdif_mutex); + + chs = substream->runtime->channels; + chan = chs ? (chs - 1) : 1; + + switch (chs) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT; + dataDCC2 = 0x2; + + /* set the Audio InforFrame Channel Allocation */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ + if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); + + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_STREAM_FORMAT, format); + + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + + for (i = 0; i < 4; i++) { + if (chs == 2) + channel_id = 0; + else + channel_id = i * 2; + + /* turn off SPDIF once; + *otherwise the IEC958 bits won't be updated + */ + if (codec->spdif_status_reset && + (codec->spdif_ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + /* set the stream id */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_CHANNEL_STREAMID, + (stream_tag << 4) | channel_id); + /* set the stream format */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_STREAM_FORMAT, + format); + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && + (codec->spdif_ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + } + + /* set the Audio Info Frame Checksum */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); + + mutex_unlock(&codec->spdif_mutex); + return 0; +} + +static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = nvhdmi_master_con_nid_7x, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, + .ops = { + .open = simple_playback_pcm_open, + .close = nvhdmi_8ch_7x_pcm_close, + .prepare = nvhdmi_8ch_7x_pcm_prepare + }, +}; + +static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = nvhdmi_master_con_nid_7x, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, + .ops = { + .open = simple_playback_pcm_open, + .close = simple_playback_pcm_close, + .prepare = simple_playback_pcm_prepare + }, +}; + +static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { + .build_controls = generic_hdmi_build_controls, + .build_pcms = generic_hdmi_build_pcms, + .init = nvhdmi_7x_init, + .free = generic_hdmi_free, +}; + +static struct hda_codec_ops nvhdmi_patch_ops_2ch = { + .build_controls = generic_hdmi_build_controls, + .build_pcms = generic_hdmi_build_pcms, + .init = nvhdmi_7x_init, + .free = generic_hdmi_free, +}; + +static int patch_nvhdmi_8ch_89(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err = patch_generic_hdmi(codec); + + if (err < 0) + return err; + spec = codec->spec; + spec->old_pin_detect = 1; + return 0; +} + +static int patch_nvhdmi_2ch(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; + spec->old_pin_detect = 1; + spec->num_cvts = 1; + spec->cvt[0] = nvhdmi_master_con_nid_7x; + spec->pcm_playback = &nvhdmi_pcm_playback_2ch; + + codec->patch_ops = nvhdmi_patch_ops_2ch; + + return 0; +} + +static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err = patch_nvhdmi_2ch(codec); + + if (err < 0) + return err; + spec = codec->spec; + spec->multiout.max_channels = 8; + spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x; + codec->patch_ops = nvhdmi_patch_ops_8ch_7x; + return 0; +} + +/* + * ATI-specific implementations + * + * FIXME: we may omit the whole this and use the generic code once after + * it's confirmed to work. + */ + +#define ATIHDMI_CVT_NID 0x02 /* audio converter */ +#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ + +static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int chans = substream->runtime->channels; + int i, err; + + err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, + substream); + if (err < 0) + return err; + snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT, + chans - 1); + /* FIXME: XXX */ + for (i = 0; i < chans; i++) { + snd_hda_codec_write(codec, spec->cvt[0], 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (i << 4) | i); + } + return 0; +} + +static struct hda_pcm_stream atihdmi_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = ATIHDMI_CVT_NID, + .ops = { + .open = simple_playback_pcm_open, + .close = simple_playback_pcm_close, + .prepare = atihdmi_playback_pcm_prepare + }, +}; + +static struct hda_verb atihdmi_basic_init[] = { + /* enable digital output on pin widget */ + { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {} /* terminator */ +}; + +static int atihdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + snd_hda_sequence_write(codec, atihdmi_basic_init); + /* SI codec requires to unmute the pin */ + if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, spec->pin[0], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + return 0; +} + +static struct hda_codec_ops atihdmi_patch_ops = { + .build_controls = generic_hdmi_build_controls, + .build_pcms = generic_hdmi_build_pcms, + .init = atihdmi_init, + .free = generic_hdmi_free, +}; + + +static int patch_atihdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = ATIHDMI_CVT_NID; + spec->num_cvts = 1; + spec->cvt[0] = ATIHDMI_CVT_NID; + spec->pin[0] = ATIHDMI_PIN_NID; + spec->pcm_playback = &atihdmi_pcm_digital_playback; + + codec->patch_ops = atihdmi_patch_ops; + + return 0; +} + + +/* + * patch entries + */ +static struct hda_codec_preset snd_hda_preset_hdmi[] = { +{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, +{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, +{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, +{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, +{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, +{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, +{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, +{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, +{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, +{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, +{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, +{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, +{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, +{} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:1002793c"); +MODULE_ALIAS("snd-hda-codec-id:10027919"); +MODULE_ALIAS("snd-hda-codec-id:1002791a"); +MODULE_ALIAS("snd-hda-codec-id:1002aa01"); +MODULE_ALIAS("snd-hda-codec-id:10951390"); +MODULE_ALIAS("snd-hda-codec-id:10951392"); +MODULE_ALIAS("snd-hda-codec-id:10de0002"); +MODULE_ALIAS("snd-hda-codec-id:10de0003"); +MODULE_ALIAS("snd-hda-codec-id:10de0005"); +MODULE_ALIAS("snd-hda-codec-id:10de0006"); +MODULE_ALIAS("snd-hda-codec-id:10de0007"); +MODULE_ALIAS("snd-hda-codec-id:10de000a"); +MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000c"); +MODULE_ALIAS("snd-hda-codec-id:10de000d"); +MODULE_ALIAS("snd-hda-codec-id:10de0010"); +MODULE_ALIAS("snd-hda-codec-id:10de0011"); +MODULE_ALIAS("snd-hda-codec-id:10de0012"); +MODULE_ALIAS("snd-hda-codec-id:10de0013"); +MODULE_ALIAS("snd-hda-codec-id:10de0014"); +MODULE_ALIAS("snd-hda-codec-id:10de0018"); +MODULE_ALIAS("snd-hda-codec-id:10de0019"); +MODULE_ALIAS("snd-hda-codec-id:10de001a"); +MODULE_ALIAS("snd-hda-codec-id:10de001b"); +MODULE_ALIAS("snd-hda-codec-id:10de001c"); +MODULE_ALIAS("snd-hda-codec-id:10de0040"); +MODULE_ALIAS("snd-hda-codec-id:10de0041"); +MODULE_ALIAS("snd-hda-codec-id:10de0042"); +MODULE_ALIAS("snd-hda-codec-id:10de0043"); +MODULE_ALIAS("snd-hda-codec-id:10de0044"); +MODULE_ALIAS("snd-hda-codec-id:10de0067"); +MODULE_ALIAS("snd-hda-codec-id:10de8001"); +MODULE_ALIAS("snd-hda-codec-id:17e80047"); +MODULE_ALIAS("snd-hda-codec-id:80860054"); +MODULE_ALIAS("snd-hda-codec-id:80862801"); +MODULE_ALIAS("snd-hda-codec-id:80862802"); +MODULE_ALIAS("snd-hda-codec-id:80862803"); +MODULE_ALIAS("snd-hda-codec-id:80862804"); +MODULE_ALIAS("snd-hda-codec-id:80862805"); +MODULE_ALIAS("snd-hda-codec-id:808629fb"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HDMI HD-audio codec"); +MODULE_ALIAS("snd-hda-codec-intelhdmi"); +MODULE_ALIAS("snd-hda-codec-nvhdmi"); +MODULE_ALIAS("snd-hda-codec-atihdmi"); + +static struct hda_codec_preset_list intel_list = { + .preset = snd_hda_preset_hdmi, + .owner = THIS_MODULE, +}; + +static int __init patch_hdmi_init(void) +{ + return snd_hda_add_codec_preset(&intel_list); +} + +static void __exit patch_hdmi_exit(void) +{ + snd_hda_delete_codec_preset(&intel_list); +} + +module_init(patch_hdmi_init) +module_exit(patch_hdmi_exit) diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c deleted file mode 100644 index d382d3c81c0f..000000000000 --- a/sound/pci/hda/patch_intelhdmi.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * - * patch_intelhdmi.c - Patch for Intel HDMI codecs - * - * Copyright(c) 2008 Intel Corporation. All rights reserved. - * - * Authors: - * Jiang Zhe <zhe.jiang@intel.com> - * Wu Fengguang <wfg@linux.intel.com> - * - * Maintained by: - * Wu Fengguang <wfg@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -/* - * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device - * could support two independent pipes, each of them can be connected to one or - * more ports (DVI, HDMI or DisplayPort). - * - * The HDA correspondence of pipes/ports are converter/pin nodes. - */ -#define MAX_HDMI_CVTS 3 -#define MAX_HDMI_PINS 3 - -#include "patch_hdmi.c" - -static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = { - "INTEL HDMI 0", - "INTEL HDMI 1", - "INTEL HDMI 2", -}; - -/* - * HDMI callbacks - */ - -static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - hdmi_set_channel_count(codec, hinfo->nid, - substream->runtime->channels); - - hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - - return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); -} - -static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static struct hda_pcm_stream intel_hdmi_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .ops = { - .open = hdmi_pcm_open, - .prepare = intel_hdmi_playback_pcm_prepare, - .cleanup = intel_hdmi_playback_pcm_cleanup, - }, -}; - -static int intel_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - int i; - - codec->num_pcms = spec->num_cvts; - codec->pcm_info = info; - - for (i = 0; i < codec->num_pcms; i++, info++) { - unsigned int chans; - - chans = get_wcaps(codec, spec->cvt[i]); - chans = get_wcaps_channels(chans); - - info->name = intel_hdmi_pcm_names[i]; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - intel_hdmi_pcm_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; - } - - return 0; -} - -static int intel_hdmi_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err; - int i; - - for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); - if (err < 0) - return err; - } - - return 0; -} - -static int intel_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - for (i = 0; spec->pin[i]; i++) { - hdmi_enable_output(codec, spec->pin[i]); - snd_hda_codec_write(codec, spec->pin[i], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | spec->pin[i]); - } - return 0; -} - -static void intel_hdmi_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); - - kfree(spec); -} - -static struct hda_codec_ops intel_hdmi_patch_ops = { - .init = intel_hdmi_init, - .free = intel_hdmi_free, - .build_pcms = intel_hdmi_build_pcms, - .build_controls = intel_hdmi_build_controls, - .unsol_event = hdmi_unsol_event, -}; - -static int patch_intel_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int i; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - if (hdmi_parse_codec(codec) < 0) { - codec->spec = NULL; - kfree(spec); - return -EINVAL; - } - codec->patch_ops = intel_hdmi_patch_ops; - - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); - - init_channel_allocations(); - - return 0; -} - -static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { -{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi }, -{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, -{} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:808629fb"); -MODULE_ALIAS("snd-hda-codec-id:80862801"); -MODULE_ALIAS("snd-hda-codec-id:80862802"); -MODULE_ALIAS("snd-hda-codec-id:80862803"); -MODULE_ALIAS("snd-hda-codec-id:80862804"); -MODULE_ALIAS("snd-hda-codec-id:80862805"); -MODULE_ALIAS("snd-hda-codec-id:80860054"); -MODULE_ALIAS("snd-hda-codec-id:10951392"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Intel HDMI HD-audio codec"); - -static struct hda_codec_preset_list intel_list = { - .preset = snd_hda_preset_intelhdmi, - .owner = THIS_MODULE, -}; - -static int __init patch_intelhdmi_init(void) -{ - return snd_hda_add_codec_preset(&intel_list); -} - -static void __exit patch_intelhdmi_exit(void) -{ - snd_hda_delete_codec_preset(&intel_list); -} - -module_init(patch_intelhdmi_init) -module_exit(patch_intelhdmi_exit) diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c deleted file mode 100644 index f636870dc718..000000000000 --- a/sound/pci/hda/patch_nvhdmi.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for NVIDIA HDMI codecs - * - * Copyright (c) 2008 NVIDIA Corp. All rights reserved. - * Copyright (c) 2008 Wei Ni <wni@nvidia.com> - * - * - * This driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This driver 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/delay.h> -#include <linux/slab.h> -#include <sound/core.h> -#include "hda_codec.h" -#include "hda_local.h" - -#define MAX_HDMI_CVTS 1 -#define MAX_HDMI_PINS 1 - -#include "patch_hdmi.c" - -static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = { - "NVIDIA HDMI", -}; - -/* define below to restrict the supported rates and formats */ -/* #define LIMITED_RATE_FMT_SUPPORT */ - -enum HDACodec { - HDA_CODEC_NVIDIA_MCP7X, - HDA_CODEC_NVIDIA_MCP89, - HDA_CODEC_NVIDIA_GT21X, - HDA_CODEC_INVALID -}; - -#define Nv_VERB_SET_Channel_Allocation 0xF79 -#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A -#define Nv_VERB_SET_Audio_Protection_On 0xF98 -#define Nv_VERB_SET_Audio_Protection_Off 0xF99 - -#define nvhdmi_master_con_nid_7x 0x04 -#define nvhdmi_master_pin_nid_7x 0x05 - -#define nvhdmi_master_con_nid_89 0x04 -#define nvhdmi_master_pin_nid_89 0x05 - -static hda_nid_t nvhdmi_con_nids_7x[4] = { - /*front, rear, clfe, rear_surr */ - 0x6, 0x8, 0xa, 0xc, -}; - -static struct hda_verb nvhdmi_basic_init_7x[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -#ifdef LIMITED_RATE_FMT_SUPPORT -/* support only the safe format and rate */ -#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 -#define SUPPORTED_MAXBPS 16 -#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE -#else -/* support all rates and formats */ -#define SUPPORTED_RATES \ - (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ - SNDRV_PCM_RATE_192000) -#define SUPPORTED_MAXBPS 24 -#define SUPPORTED_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) -#endif - -/* - * Controls - */ -static int nvhdmi_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err; - int i; - - if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) - || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { - for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->cvt[i]); - if (err < 0) - return err; - } - } else { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - } - - return 0; -} - -static int nvhdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i; - if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) - || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { - for (i = 0; spec->pin[i]; i++) { - hdmi_enable_output(codec, spec->pin[i]); - snd_hda_codec_write(codec, spec->pin[i], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | spec->pin[i]); - } - } else { - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x); - } - return 0; -} - -static void nvhdmi_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) - || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); - } - - kfree(spec); -} - -/* - * Digital out - */ -static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, - 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - for (i = 0; i < 4; i++) { - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_STREAM_FORMAT, 0); - } - - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - hdmi_set_channel_count(codec, hinfo->nid, - substream->runtime->channels); - - hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - - return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); -} - -static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int chs; - unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; - int i; - - mutex_lock(&codec->spdif_mutex); - - chs = substream->runtime->channels; - chan = chs ? (chs - 1) : 1; - - switch (chs) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } - dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT; - dataDCC2 = 0x2; - - /* set the Audio InforFrame Channel Allocation */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); - - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); - - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_STREAM_FORMAT, format); - - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - - for (i = 0; i < 4; i++) { - if (chs == 2) - channel_id = 0; - else - channel_id = i * 2; - - /* turn off SPDIF once; - *otherwise the IEC958 bits won't be updated - */ - if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); - /* set the stream id */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_CHANNEL_STREAMID, - (stream_tag << 4) | channel_id); - /* set the stream format */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_STREAM_FORMAT, - format); - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - } - - /* set the Audio Info Frame Checksum */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); - - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = { - .substreams = 1, - .channels_min = 2, - .ops = { - .open = hdmi_pcm_open, - .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89, - .cleanup = nvhdmi_playback_pcm_cleanup, - }, -}; - -static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = nvhdmi_dig_playback_pcm_open, - .close = nvhdmi_dig_playback_pcm_close_8ch_7x, - .prepare = nvhdmi_dig_playback_pcm_prepare_8ch - }, -}; - -static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = nvhdmi_dig_playback_pcm_open, - .close = nvhdmi_dig_playback_pcm_close_2ch, - .prepare = nvhdmi_dig_playback_pcm_prepare_2ch - }, -}; - -static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - int i; - - codec->num_pcms = spec->num_cvts; - codec->pcm_info = info; - - for (i = 0; i < codec->num_pcms; i++, info++) { - unsigned int chans; - - chans = get_wcaps(codec, spec->cvt[i]); - chans = get_wcaps_channels(chans); - - info->name = nvhdmi_pcm_names[i]; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] - = nvhdmi_pcm_digital_playback_8ch_89; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; - } - - return 0; -} - -static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "NVIDIA HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] - = nvhdmi_pcm_digital_playback_8ch_7x; - - return 0; -} - -static int nvhdmi_build_pcms_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "NVIDIA HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] - = nvhdmi_pcm_digital_playback_2ch; - - return 0; -} - -static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = { - .build_controls = nvhdmi_build_controls, - .build_pcms = nvhdmi_build_pcms_8ch_89, - .init = nvhdmi_init, - .free = nvhdmi_free, - .unsol_event = hdmi_unsol_event, -}; - -static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { - .build_controls = nvhdmi_build_controls, - .build_pcms = nvhdmi_build_pcms_8ch_7x, - .init = nvhdmi_init, - .free = nvhdmi_free, -}; - -static struct hda_codec_ops nvhdmi_patch_ops_2ch = { - .build_controls = nvhdmi_build_controls, - .build_pcms = nvhdmi_build_pcms_2ch, - .init = nvhdmi_init, - .free = nvhdmi_free, -}; - -static int patch_nvhdmi_8ch_89(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int i; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - spec->codec_type = HDA_CODEC_NVIDIA_MCP89; - spec->old_pin_detect = 1; - - if (hdmi_parse_codec(codec) < 0) { - codec->spec = NULL; - kfree(spec); - return -EINVAL; - } - codec->patch_ops = nvhdmi_patch_ops_8ch_89; - - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); - - init_channel_allocations(); - - return 0; -} - -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; - spec->codec_type = HDA_CODEC_NVIDIA_MCP7X; - spec->old_pin_detect = 1; - - codec->patch_ops = nvhdmi_patch_ops_8ch_7x; - - return 0; -} - -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; - spec->codec_type = HDA_CODEC_NVIDIA_MCP7X; - spec->old_pin_detect = 1; - - codec->patch_ops = nvhdmi_patch_ops_2ch; - - return 0; -} - -/* - * patch entries - */ -static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, - { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, - {} /* terminator */ -}; - -MODULE_ALIAS("snd-hda-codec-id:10de0002"); -MODULE_ALIAS("snd-hda-codec-id:10de0003"); -MODULE_ALIAS("snd-hda-codec-id:10de0005"); -MODULE_ALIAS("snd-hda-codec-id:10de0006"); -MODULE_ALIAS("snd-hda-codec-id:10de0007"); -MODULE_ALIAS("snd-hda-codec-id:10de000a"); -MODULE_ALIAS("snd-hda-codec-id:10de000b"); -MODULE_ALIAS("snd-hda-codec-id:10de000c"); -MODULE_ALIAS("snd-hda-codec-id:10de000d"); -MODULE_ALIAS("snd-hda-codec-id:10de0010"); -MODULE_ALIAS("snd-hda-codec-id:10de0011"); -MODULE_ALIAS("snd-hda-codec-id:10de0012"); -MODULE_ALIAS("snd-hda-codec-id:10de0013"); -MODULE_ALIAS("snd-hda-codec-id:10de0014"); -MODULE_ALIAS("snd-hda-codec-id:10de0018"); -MODULE_ALIAS("snd-hda-codec-id:10de0019"); -MODULE_ALIAS("snd-hda-codec-id:10de001a"); -MODULE_ALIAS("snd-hda-codec-id:10de001b"); -MODULE_ALIAS("snd-hda-codec-id:10de001c"); -MODULE_ALIAS("snd-hda-codec-id:10de0040"); -MODULE_ALIAS("snd-hda-codec-id:10de0041"); -MODULE_ALIAS("snd-hda-codec-id:10de0042"); -MODULE_ALIAS("snd-hda-codec-id:10de0043"); -MODULE_ALIAS("snd-hda-codec-id:10de0044"); -MODULE_ALIAS("snd-hda-codec-id:10de0067"); -MODULE_ALIAS("snd-hda-codec-id:10de8001"); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec"); - -static struct hda_codec_preset_list nvhdmi_list = { - .preset = snd_hda_preset_nvhdmi, - .owner = THIS_MODULE, -}; - -static int __init patch_nvhdmi_init(void) -{ - return snd_hda_add_codec_preset(&nvhdmi_list); -} - -static void __exit patch_nvhdmi_exit(void) -{ - snd_hda_delete_codec_preset(&nvhdmi_list); -} - -module_init(patch_nvhdmi_init) -module_exit(patch_nvhdmi_exit) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2cd1ae809e46..5f00589cb791 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/pci.h> #include <sound/core.h> +#include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" @@ -282,6 +283,12 @@ struct alc_mic_route { unsigned char amix_idx; }; +struct alc_jack { + hda_nid_t nid; + int type; + struct snd_jack *jack; +}; + #define MUX_IDX_UNDEF ((unsigned char)-1) struct alc_customize_define { @@ -294,6 +301,7 @@ struct alc_customize_define { unsigned int platform_type:1; unsigned int swap:1; unsigned int override:1; + unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; struct alc_spec { @@ -357,6 +365,9 @@ struct alc_spec { /* PCM information */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ + /* jack detection */ + struct snd_array jacks; + /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; @@ -383,6 +394,7 @@ struct alc_spec { unsigned int no_analog :1; /* digital I/O only */ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ int init_amp; + int codec_variant; /* flag for other variants */ /* for virtual master */ hda_nid_t vmaster_nid; @@ -846,7 +858,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, { unsigned int val = PIN_IN; - if (auto_pin_type <= AUTO_PIN_FRONT_MIC) { + if (auto_pin_type == AUTO_PIN_MIC) { unsigned int pincap; unsigned int oldval; oldval = snd_hda_codec_read(codec, nid, 0, @@ -866,6 +878,28 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } +static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (!cfg->line_outs) { + while (cfg->line_outs < AUTO_CFG_MAX_OUTS && + cfg->line_out_pins[cfg->line_outs]) + cfg->line_outs++; + } + if (!cfg->speaker_outs) { + while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS && + cfg->speaker_pins[cfg->speaker_outs]) + cfg->speaker_outs++; + } + if (!cfg->hp_outs) { + while (cfg->hp_outs < AUTO_CFG_MAX_OUTS && + cfg->hp_pins[cfg->hp_outs]) + cfg->hp_outs++; + } +} + /* */ static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix) @@ -934,6 +968,8 @@ static void setup_preset(struct hda_codec *codec, if (preset->setup) preset->setup(codec); + + alc_fixup_autocfg_pin_nums(codec); } /* Enable GPIO mask and set output */ @@ -990,25 +1026,136 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } -static void alc_automute_pin(struct hda_codec *codec) +#ifdef CONFIG_SND_HDA_INPUT_JACK +static void alc_free_jack_priv(struct snd_jack *jack) +{ + struct alc_jack *jacks = jack->private_data; + jacks->nid = 0; + jacks->jack = NULL; +} + +static int alc_add_jack(struct hda_codec *codec, + hda_nid_t nid, int type) +{ + struct alc_spec *spec; + struct alc_jack *jack; + const char *name; + int err; + + spec = codec->spec; + snd_array_init(&spec->jacks, sizeof(*jack), 32); + jack = snd_array_new(&spec->jacks); + if (!jack) + return -ENOMEM; + + jack->nid = nid; + jack->type = type; + name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; + + err = snd_jack_new(codec->bus->card, name, type, &jack->jack); + if (err < 0) + return err; + jack->jack->private_data = jack; + jack->jack->private_free = alc_free_jack_priv; + return 0; +} + +static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - unsigned int nid = spec->autocfg.hp_pins[0]; + struct alc_jack *jacks = spec->jacks.list; + + if (jacks) { + int i; + for (i = 0; i < spec->jacks.used; i++) { + if (jacks->nid == nid) { + unsigned int present; + present = snd_hda_jack_detect(codec, nid); + + present = (present) ? jacks->type : 0; + + snd_jack_report(jacks->jack, present); + } + jacks++; + } + } +} + +static int alc_init_jacks(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + unsigned int hp_nid = spec->autocfg.hp_pins[0]; + unsigned int mic_nid = spec->ext_mic.pin; + + if (hp_nid) { + err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE); + if (err < 0) + return err; + alc_report_jack(codec, hp_nid); + } + + if (mic_nid) { + err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE); + if (err < 0) + return err; + alc_report_jack(codec, mic_nid); + } + + return 0; +} +#else +static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid) +{ +} + +static inline int alc_init_jacks(struct hda_codec *codec) +{ + return 0; +} +#endif + +static void alc_automute_speaker(struct hda_codec *codec, int pinctl) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + hda_nid_t nid; int i; - if (!nid) - return; - spec->jack_present = snd_hda_jack_detect(codec, nid); + spec->jack_present = 0; + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + nid = spec->autocfg.hp_pins[i]; + if (!nid) + break; + if (snd_hda_jack_detect(codec, nid)) { + spec->jack_present = 1; + break; + } + alc_report_jack(codec, spec->autocfg.hp_pins[i]); + } + + mute = spec->jack_present ? HDA_AMP_MUTE : 0; + /* Toggle internal speakers muting */ for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { nid = spec->autocfg.speaker_pins[i]; if (!nid) break; - snd_hda_codec_write(codec, nid, 0, + if (pinctl) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->jack_present ? 0 : PIN_OUT); + } else { + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } } } +static void alc_automute_pin(struct hda_codec *codec) +{ + alc_automute_speaker(codec, 1); +} + static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid) { @@ -1090,6 +1237,7 @@ static void alc_mic_automute(struct hda_codec *codec) AC_VERB_SET_CONNECT_SEL, alive->mux_idx); } + alc_report_jack(codec, spec->ext_mic.pin); /* FIXME: analog mixer */ } @@ -1236,24 +1384,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) static void alc_init_auto_hp(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; - if (!spec->autocfg.hp_pins[0]) - return; + if (!cfg->hp_pins[0]) { + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + return; + } - if (!spec->autocfg.speaker_pins[0]) { - if (spec->autocfg.line_out_pins[0] && - spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->autocfg.speaker_pins[0] = - spec->autocfg.line_out_pins[0]; - else + if (!cfg->speaker_pins[0]) { + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) return; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = cfg->line_outs; } - snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", - spec->autocfg.hp_pins[0]); - snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0, + if (!cfg->hp_pins[0]) { + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = cfg->line_outs; + } + + for (i = 0; i < cfg->hp_outs; i++) { + snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", + cfg->hp_pins[i]); + snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT); + } spec->unsol_event = alc_sku_unsol_event; } @@ -1265,30 +1424,28 @@ static void alc_init_auto_mic(struct hda_codec *codec) int i; /* there must be only two mic inputs exclusively */ - for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) - if (cfg->input_pins[i]) + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN) return; fixed = ext = 0; - for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) { - hda_nid_t nid = cfg->input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; unsigned int defcfg; - if (!nid) - return; defcfg = snd_hda_codec_get_pincfg(codec, nid); - switch (get_defcfg_connect(defcfg)) { - case AC_JACK_PORT_FIXED: + switch (snd_hda_get_input_pin_attr(defcfg)) { + case INPUT_PIN_ATTR_INT: if (fixed) return; /* already occupied */ fixed = nid; break; - case AC_JACK_PORT_COMPLEX: + case INPUT_PIN_ATTR_UNUSED: + return; /* invalid entry */ + default: if (ext) return; /* already occupied */ ext = nid; break; - default: - return; /* invalid entry */ } } if (!ext || !fixed) @@ -1308,6 +1465,11 @@ static void alc_init_auto_mic(struct hda_codec *codec) spec->unsol_event = alc_sku_unsol_event; } +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2) + static int alc_auto_parse_customize_define(struct hda_codec *codec) { unsigned int ass, tmp, i; @@ -1316,6 +1478,13 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec) spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; + } + ass = codec->subsystem_id & 0xffff; if (ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; @@ -1383,6 +1552,13 @@ static int alc_subsystem_id(struct hda_codec *codec, unsigned nid; struct alc_spec *spec = codec->spec; + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return 0; + goto do_sku; + } + ass = codec->subsystem_id & 0xffff; if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) goto do_sku; @@ -1502,6 +1678,7 @@ struct alc_pincfg { }; struct alc_fixup { + unsigned int sku; const struct alc_pincfg *pins; const struct hda_verb *verbs; }; @@ -1512,12 +1689,22 @@ static void alc_pick_fixup(struct hda_codec *codec, int pre_init) { const struct alc_pincfg *cfg; + struct alc_spec *spec; quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); if (!quirk) return; fix += quirk->value; cfg = fix->pins; + if (pre_init && fix->sku) { +#ifdef CONFIG_SND_DEBUG_VERBOSE + snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n", + codec->chip_name, quirk->name); +#endif + spec = codec->spec; + spec->cdefine.sku_cfg = fix->sku; + spec->cdefine.fixup = 1; + } if (pre_init && cfg) { #ifdef CONFIG_SND_DEBUG_VERBOSE snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n", @@ -1546,6 +1733,15 @@ static int alc_read_coef_idx(struct hda_codec *codec, return val; } +static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, + unsigned int coef_val) +{ + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, + coef_idx); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, + coef_val); +} + /* set right pin controls for digital I/O */ static void alc_auto_init_digital(struct hda_codec *codec) { @@ -1594,12 +1790,22 @@ static void alc_auto_parse_digital(struct hda_codec *codec) } if (spec->autocfg.dig_in_pin) { - hda_nid_t dig_nid; - err = snd_hda_get_connections(codec, - spec->autocfg.dig_in_pin, - &dig_nid, 1); - if (err > 0) - spec->dig_in_nid = dig_nid; + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } } } @@ -1713,31 +1919,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { static void alc_automute_amp(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - unsigned int mute; - hda_nid_t nid; - int i; - - spec->jack_present = 0; - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - nid = spec->autocfg.hp_pins[i]; - if (!nid) - break; - if (snd_hda_jack_detect(codec, nid)) { - spec->jack_present = 1; - break; - } - } - - mute = spec->jack_present ? HDA_AMP_MUTE : 0; - /* Toggle internal speakers muting */ - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - nid = spec->autocfg.speaker_pins[i]; - if (!nid) - break; - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } + alc_automute_speaker(codec, 0); } static void alc_automute_amp_unsol_event(struct hda_codec *codec, @@ -3592,10 +3774,7 @@ static int alc_init(struct hda_codec *codec) if (spec->init_hook) spec->init_hook(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (codec->patch_ops.check_power_status) - codec->patch_ops.check_power_status(codec, 0x01); -#endif + hda_call_check_power_status(codec, 0x01); return 0; } @@ -3991,10 +4170,7 @@ static int alc_resume(struct hda_codec *codec) codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (codec->patch_ops.check_power_status) - codec->patch_ops.check_power_status(codec, 0x01); -#endif + hda_call_check_power_status(codec, 0x01); return 0; } #endif @@ -4719,7 +4895,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = { /* add dynamic controls */ static int add_control(struct alc_spec *spec, int type, const char *name, - unsigned long val) + int cidx, unsigned long val) { struct snd_kcontrol_new *knew; @@ -4731,6 +4907,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + knew->index = cidx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; @@ -4739,17 +4916,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name, static int add_control_with_pfx(struct alc_spec *spec, int type, const char *pfx, const char *dir, - const char *sfx, unsigned long val) + const char *sfx, int cidx, unsigned long val) { char name[32]; snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - return add_control(spec, type, name, val); + return add_control(spec, type, name, cidx, val); } -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val) +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) #define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) #define alc880_fixed_pin_idx(nid) ((nid) - 0x14) @@ -4902,16 +5083,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, /* create input playback/capture controls for the given pin */ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, - const char *ctlname, + const char *ctlname, int ctlidx, int idx, hda_nid_t mix_nid) { int err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -4932,20 +5113,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx; + int i, err, idx, type, type_idx = 0; - for (i = 0; i < AUTO_PIN_LAST; i++) { + for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; + const char *label; - pin = cfg->input_pins[i]; + pin = cfg->inputs[i].pin; if (!alc_is_input_pin(codec, pin)) continue; + type = cfg->inputs[i].type; + if (i > 0 && type == cfg->inputs[i - 1].type) + type_idx++; + else + type_idx = 0; + label = hda_get_autocfg_input_label(codec, cfg, i); if (mixer) { idx = get_connection_index(codec, mixer, pin); if (idx >= 0) { err = new_analog_input(spec, pin, - auto_pin_cfg_labels[i], + label, type_idx, idx, mixer); if (err < 0) return err; @@ -4957,12 +5145,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec, idx = get_connection_index(codec, cap1, pin); if (idx < 0 && cap2) idx = get_connection_index(codec, cap2, pin); - if (idx >= 0) { - imux->items[imux->num_items].label = - auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } + if (idx >= 0) + snd_hda_add_imux_item(imux, label, idx, NULL); } return 0; } @@ -5034,12 +5218,13 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec) static void alc880_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, i); + alc_set_input_pin(codec, nid, cfg->inputs[i].type); if (nid != ALC880_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) snd_hda_codec_write(codec, nid, 0, @@ -5204,19 +5389,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) static void fixup_single_adc(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin = 0; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; /* search for the input pin; there must be only one */ - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (spec->autocfg.input_pins[i]) { - pin = spec->autocfg.input_pins[i]; - break; - } - } - if (!pin) + if (cfg->num_inputs != 1) return; - i = init_capsrc_for_pin(codec, pin); + i = init_capsrc_for_pin(codec, cfg->inputs[0].pin); if (i >= 0) { /* use only this ADC */ if (spec->capsrc_nids) @@ -5269,6 +5448,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, int num_nids) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int n; hda_nid_t fallback_adc = 0, fallback_cap = 0; @@ -5294,10 +5474,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, fallback_adc = adc; fallback_cap = cap; } - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; - if (!nid) - continue; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; for (j = 0; j < nconns; j++) { if (conn[j] == nid) break; @@ -5305,7 +5483,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, if (j >= nconns) break; } - if (i >= AUTO_PIN_LAST) { + if (i >= cfg->num_inputs) { int num_adcs = spec->num_adc_nids; spec->private_adc_nids[num_adcs] = adc; spec->private_capsrc_nids[num_adcs] = cap; @@ -5334,6 +5512,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, static struct snd_pci_quirk beep_white_list[] = { SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), {} }; @@ -6672,12 +6851,13 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) static void alc260_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (nid >= 0x12) { - alc_set_input_pin(codec, nid, i); + alc_set_input_pin(codec, nid, cfg->inputs[i].type); if (nid != ALC260_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) snd_hda_codec_write(codec, nid, 0, @@ -6799,14 +6979,12 @@ enum { PINFIX_HP_DC5750, }; -static struct alc_pincfg alc260_hp_dc5750_pinfix[] = { - { 0x11, 0x90130110 }, /* speaker */ - { } -}; - static const struct alc_fixup alc260_fixups[] = { [PINFIX_HP_DC5750] = { - .pins = alc260_hp_dc5750_pinfix + .pins = (const struct alc_pincfg[]) { + { 0x11, 0x90130110 }, /* speaker */ + { } + } }, }; @@ -10450,32 +10628,33 @@ static struct alc_config_preset alc882_presets[] = { enum { PINFIX_ABIT_AW9D_MAX, PINFIX_PB_M5210, -}; - -static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } -}; - -static const struct hda_verb pb_m5210_verbs[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, - {} + PINFIX_ACER_ASPIRE_7736, }; static const struct alc_fixup alc882_fixups[] = { [PINFIX_ABIT_AW9D_MAX] = { - .pins = alc882_abit_aw9d_pinfix + .pins = (const struct alc_pincfg[]) { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ + { } + } }, [PINFIX_PB_M5210] = { - .verbs = pb_m5210_verbs + .verbs = (const struct hda_verb[]) { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + {} + } + }, + [PINFIX_ACER_ASPIRE_7736] = { + .sku = ALC_FIXUP_SKU_IGNORE, }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), + SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), {} }; @@ -10524,16 +10703,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t pin, dac; + int i; - pin = spec->autocfg.hp_pins[0]; - if (pin) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; dac = spec->multiout.hp_nid; if (!dac) dac = spec->multiout.dac_nids[0]; /* to front */ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); } - pin = spec->autocfg.speaker_pins[0]; - if (pin) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; dac = spec->multiout.extra_out_nid[0]; if (!dac) dac = spec->multiout.dac_nids[0]; /* to front */ @@ -10544,13 +10728,12 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) static void alc882_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; - if (!nid) - continue; - alc_set_input_pin(codec, nid, i); + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + alc_set_input_pin(codec, nid, cfg->inputs[i].type); if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -10612,24 +10795,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) static int alc_auto_add_mic_boost(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int err; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; hda_nid_t nid; - nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; - if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Mic Boost", - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; - if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Front Mic Boost", + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + char label[32]; + snprintf(label, sizeof(label), "%s Boost", + hda_get_autocfg_input_label(codec, cfg, i)); + err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; + if (err < 0) + return err; + } } return 0; } @@ -10715,8 +10897,6 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; - alc_auto_parse_customize_define(codec); - switch (codec->vendor_id) { case 0x10ec0882: case 0x10ec0885: @@ -10744,6 +10924,8 @@ static int patch_alc882(struct hda_codec *codec) if (board_config == ALC882_AUTO) alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1); + alc_auto_parse_customize_define(codec); + if (board_config == ALC882_AUTO) { /* automatic parse from the BIOS config */ err = alc882_parse_auto_config(codec); @@ -10824,6 +11006,8 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC882_AUTO) spec->init_hook = alc882_auto_init; + + alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc882_loopbacks; @@ -11820,7 +12004,7 @@ static int alc262_check_volbit(hda_nid_t nid) } static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vbits) + const char *pfx, int *vbits, int idx) { unsigned long val; int vbit; @@ -11835,11 +12019,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val); + return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val); } static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx) + const char *pfx, int idx) { unsigned long val; @@ -11849,7 +12033,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val); + return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val); } /* add playback controls from the parsed DAC table */ @@ -11858,7 +12042,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, { const char *pfx; int vbits; - int err; + int i, err; spec->multiout.num_dacs = 1; /* only use one dac */ spec->multiout.dac_nids = spec->private_dac_nids; @@ -11868,39 +12052,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "Master"; else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) pfx = "Speaker"; + else if (cfg->line_out_type == AUTO_PIN_HP_OUT) + pfx = "Headphone"; else pfx = "Front"; - err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx); - if (err < 0) - return err; - err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker"); - if (err < 0) - return err; - err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone"); - if (err < 0) - return err; + for (i = 0; i < 2; i++) { + err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i], + "Speaker", i); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i], + "Headphone", i); + if (err < 0) + return err; + } + } vbits = alc262_check_volbit(cfg->line_out_pins[0]) | alc262_check_volbit(cfg->speaker_pins[0]) | alc262_check_volbit(cfg->hp_pins[0]); if (vbits == 1 || vbits == 2) pfx = "Master"; /* only one mixer is used */ - else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - pfx = "Speaker"; - else - pfx = "Front"; vbits = 0; - err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits); - if (err < 0) - return err; - err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker", - &vbits); - if (err < 0) - return err; - err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone", - &vbits); - if (err < 0) - return err; + for (i = 0; i < 2; i++) { + err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, + &vbits, i); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i], + "Speaker", &vbits, i); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i], + "Headphone", &vbits, i); + if (err < 0) + return err; + } + } return 0; } @@ -12188,6 +12385,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { {} }; +/* + * Pin config fixes + */ +enum { + PINFIX_FSC_H270, +}; + +static const struct alc_fixup alc262_fixups[] = { + [PINFIX_FSC_H270] = { + .pins = (const struct alc_pincfg[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0221142f }, /* front HP */ + { 0x1b, 0x0121141f }, /* rear HP */ + { } + } + }, + [PINFIX_PB_M5210] = { + .verbs = (const struct hda_verb[]) { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + {} + } + }, +}; + +static struct snd_pci_quirk alc262_fixup_tbl[] = { + SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270), + {} +}; + #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc262_loopbacks alc880_loopbacks @@ -12611,6 +12837,9 @@ static int patch_alc262(struct hda_codec *codec) board_config = ALC262_AUTO; } + if (board_config == ALC262_AUTO) + alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1); + if (board_config == ALC262_AUTO) { /* automatic parse from the BIOS config */ err = alc262_parse_auto_config(codec); @@ -12679,11 +12908,16 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->no_analog && has_cdefine_beep(codec)) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (board_config == ALC262_AUTO) + alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0); + spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; if (board_config == ALC262_AUTO) spec->init_hook = alc262_auto_init; + + alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc262_loopbacks; @@ -13299,8 +13533,10 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, static void alc268_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.line_out_pins[0]; - if (nid) { + int i; + + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); alc268_auto_set_output_and_unmute(codec, nid, pin_type); } @@ -13310,13 +13546,19 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t pin; + int i; - pin = spec->autocfg.hp_pins[0]; - if (pin) + for (i = 0; i < spec->autocfg.hp_outs; i++) { + pin = spec->autocfg.hp_pins[i]; alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); - pin = spec->autocfg.speaker_pins[0]; - if (pin) + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + pin = spec->autocfg.speaker_pins[i]; alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); + } + if (spec->autocfg.mono_out_pin) + snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); } static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) @@ -13755,6 +13997,8 @@ static int patch_alc268(struct hda_codec *codec) if (board_config == ALC268_AUTO) spec->init_hook = alc268_auto_init; + alc_init_jacks(codec); + return 0; } @@ -14121,6 +14365,7 @@ static void alc269_speaker_automute(struct hda_codec *codec) HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, HDA_AMP_MUTE, bits); + alc_report_jack(codec, nid); } /* unsolicited event for HP jack sensing */ @@ -14375,6 +14620,13 @@ static int alc275_setup_dual_adc(struct hda_codec *codec) return 0; } +/* different alc269-variants */ +enum { + ALC269_TYPE_NORMAL, + ALC269_TYPE_ALC259, + ALC269_TYPE_ALC271X, +}; + /* * BIOS auto configuration */ @@ -14392,7 +14644,11 @@ static int alc269_parse_auto_config(struct hda_codec *codec) err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - err = alc269_auto_create_input_ctls(codec, &spec->autocfg); + if (spec->codec_variant == ALC269_TYPE_NORMAL) + err = alc269_auto_create_input_ctls(codec, &spec->autocfg); + else + err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0, + 0x22, 0); if (err < 0) return err; @@ -14403,7 +14659,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { + if (spec->codec_variant != ALC269_TYPE_NORMAL) { add_verb(spec, alc269vb_init_verbs); alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); } else { @@ -14450,23 +14706,85 @@ static void alc269_auto_init(struct hda_codec *codec) alc_inithook(codec); } +#ifdef SND_HDA_NEEDS_RESUME +static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) +{ + int val = alc_read_coef_idx(codec, 0x04); + if (power_up) + val |= 1 << 11; + else + val &= ~(1 << 11); + alc_write_coef_idx(codec, 0x04, val); +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc269_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct alc_spec *spec = codec->spec; + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) + alc269_toggle_power_output(codec, 0); + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + alc269_toggle_power_output(codec, 0); + msleep(150); + } + + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); + return 0; +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + +static int alc269_resume(struct hda_codec *codec) +{ + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + alc269_toggle_power_output(codec, 0); + msleep(150); + } + + codec->patch_ops.init(codec); + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { + alc269_toggle_power_output(codec, 1); + msleep(200); + } + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) + alc269_toggle_power_output(codec, 1); + + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + hda_call_check_power_status(codec, 0x01); + return 0; +} +#endif /* SND_HDA_NEEDS_RESUME */ + enum { ALC269_FIXUP_SONY_VAIO, -}; - -static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = { - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, - {} + ALC269_FIXUP_DELL_M101Z, }; static const struct alc_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .verbs = alc269_sony_vaio_fixup_verbs + .verbs = (const struct hda_verb[]) { + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, + {} + } + }, + [ALC269_FIXUP_DELL_M101Z] = { + .verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 13}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, + {} + } }, }; static struct snd_pci_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), + SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), {} }; @@ -14667,12 +14985,46 @@ static struct alc_config_preset alc269_presets[] = { }, }; +static int alc269_fill_coef(struct hda_codec *codec) +{ + int val; + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8817); + } + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8814); + } + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) { + val = alc_read_coef_idx(codec, 0x04); + /* Power up output pin */ + alc_write_coef_idx(codec, 0x04, val | (1<<11)); + } + + if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { + val = alc_read_coef_idx(codec, 0xd); + if ((val & 0x0c00) >> 10 != 0x1) { + /* Capless ramp up clock control */ + alc_write_coef_idx(codec, 0xd, val | 1<<10); + } + val = alc_read_coef_idx(codec, 0x17); + if ((val & 0x01c0) >> 6 != 0x4) { + /* Class D power on reset */ + alc_write_coef_idx(codec, 0x17, val | 1<<7); + } + } + return 0; +} + static int patch_alc269(struct hda_codec *codec) { struct alc_spec *spec; int board_config; int err; - int is_alc269vb = 0; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -14684,14 +15036,18 @@ static int patch_alc269(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ if (codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) + spec->cdefine.platform_type == 1) { alc_codec_rename(codec, "ALC271X"); - else + spec->codec_variant = ALC269_TYPE_ALC271X; + } else { alc_codec_rename(codec, "ALC259"); - is_alc269vb = 1; + spec->codec_variant = ALC269_TYPE_ALC259; + } } else alc_fix_pll_init(codec, 0x20, 0x04, 15); + alc269_fill_coef(codec); + board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, alc269_models, alc269_cfg_tbl); @@ -14748,7 +15104,7 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_capture = &alc269_pcm_digital_capture; if (!spec->adc_nids) { /* wasn't filled automatically? use default */ - if (!is_alc269vb) { + if (spec->codec_variant != ALC269_TYPE_NORMAL) { spec->adc_nids = alc269_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); spec->capsrc_nids = alc269_capsrc_nids; @@ -14770,8 +15126,16 @@ static int patch_alc269(struct hda_codec *codec) spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; +#ifdef CONFIG_SND_HDA_POWER_SAVE + codec->patch_ops.suspend = alc269_suspend; +#endif +#ifdef SND_HDA_NEEDS_RESUME + codec->patch_ops.resume = alc269_resume; +#endif if (board_config == ALC269_AUTO) spec->init_hook = alc269_auto_init; + + alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc269_loopbacks; @@ -15584,12 +15948,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) static void alc861_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (nid >= 0x0c && nid <= 0x11) - alc_set_input_pin(codec, nid, i); + alc_set_input_pin(codec, nid, cfg->inputs[i].type); } } @@ -15818,15 +16183,13 @@ enum { PINFIX_FSC_AMILO_PI1505, }; -static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ - { } -}; - static const struct alc_fixup alc861_fixups[] = { [PINFIX_FSC_AMILO_PI1505] = { - .pins = alc861_fsc_amilo_pi1505_pinfix + .pins = (const struct alc_pincfg[]) { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } + } }, }; @@ -16578,12 +16941,13 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) static void alc861vd_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, i); + alc_set_input_pin(codec, nid, cfg->inputs[i].type); if (nid != ALC861VD_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) snd_hda_codec_write(codec, nid, 0, @@ -16793,16 +17157,14 @@ enum { }; /* reset GPIO1 */ -static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } -}; - static const struct alc_fixup alc861vd_fixups[] = { [ALC660VD_FIX_ASUS_GPIO1] = { - .verbs = alc660vd_fix_asus_gpio1_verbs, + .verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + { } + } }, }; @@ -18816,12 +19178,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) static void alc662_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, i); + alc_set_input_pin(codec, nid, cfg->inputs[i].type); if (nid != ALC662_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) snd_hda_codec_write(codec, nid, 0, @@ -18913,10 +19276,40 @@ static void alc662_auto_init(struct hda_codec *codec) alc_inithook(codec); } +enum { + ALC662_FIXUP_ASPIRE, + ALC662_FIXUP_IDEAPAD, +}; + +static const struct alc_fixup alc662_fixups[] = { + [ALC662_FIXUP_ASPIRE] = { + .pins = (const struct alc_pincfg[]) { + { 0x15, 0x99130112 }, /* subwoofer */ + { } + } + }, + [ALC662_FIXUP_IDEAPAD] = { + .pins = (const struct alc_pincfg[]) { + { 0x17, 0x99130112 }, /* subwoofer */ + { } + } + }, +}; + +static struct snd_pci_quirk alc662_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), + {} +}; + + + static int patch_alc662(struct hda_codec *codec) { struct alc_spec *spec; int err, board_config; + int coef; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -18928,12 +19321,15 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); - if (alc_read_coef_idx(codec, 0) == 0x8020) + coef = alc_read_coef_idx(codec, 0); + if (coef == 0x8020 || coef == 0x8011) alc_codec_rename(codec, "ALC661"); - else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) && - codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) + else if (coef & (1 << 14) && + codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) alc_codec_rename(codec, "ALC272X"); + else if (coef == 0x4011) + alc_codec_rename(codec, "ALC656"); board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, alc662_models, @@ -18945,6 +19341,7 @@ static int patch_alc662(struct hda_codec *codec) } if (board_config == ALC662_AUTO) { + alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1); /* automatic parse from the BIOS config */ err = alc662_parse_auto_config(codec); if (err < 0) { @@ -19003,8 +19400,13 @@ static int patch_alc662(struct hda_codec *codec) spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; - if (board_config == ALC662_AUTO) + if (board_config == ALC662_AUTO) { spec->init_hook = alc662_auto_init; + alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0); + } + + alc_init_jacks(codec); + #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc662_loopbacks; @@ -19030,6 +19432,7 @@ static int patch_alc888(struct hda_codec *codec) /* * ALC680 support */ +#define ALC680_DIGIN_NID ALC880_DIGIN_NID #define ALC680_DIGOUT_NID ALC880_DIGOUT_NID #define alc680_modes alc260_modes @@ -19044,23 +19447,114 @@ static hda_nid_t alc680_adc_nids[3] = { 0x07, 0x08, 0x09 }; +/* + * Analog capture ADC cgange + */ +static void alc680_rec_autoswitch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int pin_found = 0; + int type_found = AUTO_PIN_LAST; + hda_nid_t nid; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + nid = cfg->inputs[i].pin; + if (!(snd_hda_query_pin_caps(codec, nid) & + AC_PINCAP_PRES_DETECT)) + continue; + if (snd_hda_jack_detect(codec, nid)) { + if (cfg->inputs[i].type < type_found) { + type_found = cfg->inputs[i].type; + pin_found = nid; + } + } + } + + nid = 0x07; + if (pin_found) + snd_hda_get_connections(codec, pin_found, &nid, 1); + + if (nid != spec->cur_adc) + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = nid; + snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); +} + +static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + + spec->cur_adc = 0x07; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + + alc680_rec_autoswitch(codec); + return 0; +} + +static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, 0x07); + snd_hda_codec_cleanup_stream(codec, 0x08); + snd_hda_codec_cleanup_stream(codec, 0x09); + return 0; +} + +static struct hda_pcm_stream alc680_pcm_analog_auto_capture = { + .substreams = 1, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .prepare = alc680_capture_pcm_prepare, + .cleanup = alc680_capture_pcm_cleanup + }, +}; + static struct snd_kcontrol_new alc680_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x12, 0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost", 0x19, 0, HDA_INPUT), { } }; -static struct snd_kcontrol_new alc680_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), +static struct hda_bind_ctls alc680_bind_cap_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static struct hda_bind_ctls alc680_bind_cap_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static struct snd_kcontrol_new alc680_master_capture_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch), { } /* end */ }; @@ -19068,25 +19562,60 @@ static struct snd_kcontrol_new alc680_capture_mixer[] = { * generic initialization of ADC, input mixers and output mixers */ static struct hda_verb alc680_init_verbs[] = { - /* Unmute DAC0-1 and set vol = 0 */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + { } }; +/* toggle speaker-output according to the hp-jack state */ +static void alc680_base_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x16; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.num_inputs = 2; + spec->autocfg.inputs[0].pin = 0x18; + spec->autocfg.inputs[0].type = AUTO_PIN_MIC; + spec->autocfg.inputs[1].pin = 0x19; + spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN; +} + +static void alc680_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc_automute_amp(codec); + if ((res >> 26) == ALC880_MIC_EVENT) + alc680_rec_autoswitch(codec); +} + +static void alc680_inithook(struct hda_codec *codec) +{ + alc_automute_amp(codec); + alc680_rec_autoswitch(codec); +} + /* create input playback/capture controls for the given pin */ static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) @@ -19197,13 +19726,7 @@ static void alc680_auto_init_hp_out(struct hda_codec *codec) #define alc680_pcm_analog_capture alc880_pcm_analog_capture #define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture #define alc680_pcm_digital_playback alc880_pcm_digital_playback - -static struct hda_input_mux alc680_capture_source = { - .num_items = 1, - .items = { - { "Mic", 0x0 }, - }, -}; +#define alc680_pcm_digital_capture alc880_pcm_digital_capture /* * BIOS auto configuration @@ -19218,6 +19741,7 @@ static int alc680_parse_auto_config(struct hda_codec *codec) alc680_ignore); if (err < 0) return err; + if (!spec->autocfg.line_outs) { if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { spec->multiout.max_channels = 2; @@ -19239,8 +19763,6 @@ static int alc680_parse_auto_config(struct hda_codec *codec) add_mixer(spec, spec->kctls.list); add_verb(spec, alc680_init_verbs); - spec->num_mux_defs = 1; - spec->input_mux = &alc680_capture_source; err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -19279,17 +19801,17 @@ static struct snd_pci_quirk alc680_cfg_tbl[] = { static struct alc_config_preset alc680_presets[] = { [ALC680_BASE] = { .mixers = { alc680_base_mixer }, - .cap_mixer = alc680_capture_mixer, + .cap_mixer = alc680_master_capture_mixer, .init_verbs = { alc680_init_verbs }, .num_dacs = ARRAY_SIZE(alc680_dac_nids), .dac_nids = alc680_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc680_adc_nids), - .adc_nids = alc680_adc_nids, - .hp_nid = 0x04, .dig_out_nid = ALC680_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc680_modes), .channel_mode = alc680_modes, - .input_mux = &alc680_capture_source, + .unsol_event = alc680_unsol_event, + .setup = alc680_base_setup, + .init_hook = alc680_inithook, + }, }; @@ -19333,9 +19855,9 @@ static int patch_alc680(struct hda_codec *codec) setup_preset(codec, &alc680_presets[board_config]); spec->stream_analog_playback = &alc680_pcm_analog_playback; - spec->stream_analog_capture = &alc680_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture; + spec->stream_analog_capture = &alc680_pcm_analog_auto_capture; spec->stream_digital_playback = &alc680_pcm_digital_playback; + spec->stream_digital_capture = &alc680_pcm_digital_capture; if (!spec->adc_nids) { spec->adc_nids = alc680_adc_nids; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index f3f861bd1bf8..93fa59cc60ef 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -32,6 +32,7 @@ #include <sound/core.h> #include <sound/asoundef.h> #include <sound/jack.h> +#include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_beep.h" @@ -263,6 +264,7 @@ struct sigmatel_spec { struct sigmatel_mic_route ext_mic; struct sigmatel_mic_route int_mic; + struct sigmatel_mic_route dock_mic; const char **spdif_labels; @@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = { 0x03, 0x0c, 0x20, 0x40, }; +#define STAC92HD83XXX_NUM_DMICS 2 +static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { + 0x11, 0x20, 0 +}; + #define STAC92HD83XXX_NUM_CAPS 2 static unsigned long stac92hd83xxx_capvols[] = { HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), @@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = { } static struct snd_kcontrol_new stac9200_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), @@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = { }; static struct snd_kcontrol_new stac925x_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), { } /* end */ }; @@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) struct hda_input_mux *smux = &spec->private_smux; /* check for mute support on SPDIF out */ if (wcaps & AC_WCAP_OUT_AMP) { - smux->items[smux->num_items].label = "Off"; - smux->items[smux->num_items].index = 0; - smux->num_items++; + snd_hda_add_imux_item(smux, "Off", 0, NULL); spec->spdif_mute = 1; } stac_smux_mixer.count = spec->num_smuxes; @@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec) HDA_OUTPUT, vmaster_tlv); /* correct volume offset */ vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; + /* minimum value is actually mute */ + vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; err = snd_hda_add_vmaster(codec, "Master Playback Volume", vmaster_tlv, slave_vols); if (err < 0) @@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } - for (i = 0; i < AUTO_PIN_LAST; i++) { - nid = cfg->input_pins[i]; - if (nid) { - err = stac92xx_add_jack(codec, nid, - SND_JACK_MICROPHONE); - if (err < 0) - return err; - } + for (i = 0; i < cfg->num_inputs; i++) { + nid = cfg->inputs[i].pin; + err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE); + if (err < 0) + return err; } return 0; @@ -1747,6 +1751,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP dv6", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e, + "HP DV6", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, "HP", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, @@ -2777,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, struct sigmatel_spec *spec = codec->spec; char name[22]; - if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) { + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD && nid == spec->line_switch) control = STAC_CTL_WIDGET_IO_SWITCH; @@ -2789,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, } if (control) { - strcpy(name, auto_pin_cfg_labels[idx]); + strcpy(name, hda_get_input_pin_label(codec, nid, 1)); return stac92xx_add_control(codec->spec, control, strcat(name, " Jack Mode"), nid); } @@ -2821,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; unsigned int pincap; + int i; if (cfg->line_out_type != AUTO_PIN_LINE_OUT) return 0; - nid = cfg->input_pins[AUTO_PIN_LINE]; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) - return nid; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) { + nid = cfg->inputs[i].pin; + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_OUT) + return nid; + } + } return 0; } +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid); + /* check whether the mic-input can be used as line-out */ -static hda_nid_t check_mic_out_switch(struct hda_codec *codec) +static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int def_conf, pincap; - unsigned int mic_pin; + int i; + *dac = 0; if (cfg->line_out_type != AUTO_PIN_LINE_OUT) return 0; - mic_pin = AUTO_PIN_MIC; - for (;;) { - hda_nid_t nid = cfg->input_pins[mic_pin]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; def_conf = snd_hda_codec_get_pincfg(codec, nid); /* some laptops have an internal analog microphone * which can't be used as a output */ - if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) - return nid; + if (pincap & AC_PINCAP_OUT) { + *dac = get_unassigned_dac(codec, nid); + if (*dac) + return nid; + } } - if (mic_pin == AUTO_PIN_MIC) - mic_pin = AUTO_PIN_FRONT_MIC; - else - break; } return 0; } @@ -3002,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec) } } /* add mic as output */ - nid = check_mic_out_switch(codec); - if (nid) { - dac = get_unassigned_dac(codec, nid); - if (dac) { - snd_printdd("STAC: Add mic-in 0x%x as output %d\n", - nid, cfg->line_outs); - cfg->line_out_pins[cfg->line_outs] = nid; - cfg->line_outs++; - spec->mic_switch = nid; - add_spec_dacs(spec, dac); - } + nid = check_mic_out_switch(codec, &dac); + if (nid && dac) { + snd_printdd("STAC: Add mic-in 0x%x as output %d\n", + nid, cfg->line_outs); + cfg->line_out_pins[cfg->line_outs] = nid; + cfg->line_outs++; + spec->mic_switch = nid; + add_spec_dacs(spec, dac); } snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", @@ -3202,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } - for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) { - nid = cfg->input_pins[idx]; - if (nid) { - err = stac92xx_add_jack_mode_control(codec, nid, idx); - if (err < 0) - return err; - } + for (idx = 0; idx < cfg->num_inputs; idx++) { + if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) + break; + nid = cfg->inputs[idx].pin; + err = stac92xx_add_jack_mode_control(codec, nid, idx); + if (err < 0) + return err; } return 0; @@ -3254,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) return -EINVAL; - for (i = 0; i < num_cons; i++) { - mono_mux->items[mono_mux->num_items].label = - stac92xx_mono_labels[i]; - mono_mux->items[mono_mux->num_items].index = i; - mono_mux->num_items++; - } + for (i = 0; i < num_cons; i++) + snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i, + NULL); return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, "Mono Mux", spec->mono_nid); @@ -3384,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) if (!labels) labels = stac92xx_spdif_labels; - for (i = 0; i < num_cons; i++) { - spdif_mux->items[spdif_mux->num_items].label = labels[i]; - spdif_mux->items[spdif_mux->num_items].index = i; - spdif_mux->num_items++; - } + for (i = 0; i < num_cons; i++) + snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL); return 0; } @@ -3415,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, /* create a volume assigned to the given pin (only if supported) */ /* return 1 if the volume control is created */ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, - const char *label, int direction) + const char *label, int idx, int direction) { unsigned int caps, nums; char name[32]; @@ -3432,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, if (!nums) return 0; snprintf(name, sizeof(name), "%s Capture Volume", label); - err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); if (err < 0) return err; return 1; @@ -3446,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *dimux = &spec->private_dimux; - int err, i, active_mics; + int err, i; unsigned int def_conf; - dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; - dimux->items[dimux->num_items].index = 0; - dimux->num_items++; - - active_mics = 0; - for (i = 0; i < spec->num_dmics; i++) { - /* check the validity: sometimes it's a dead vendor-spec node */ - if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i])) - != AC_WID_PIN) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]); - if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) - active_mics++; - } + snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL); for (i = 0; i < spec->num_dmics; i++) { hda_nid_t nid; - int index; + int index, type_idx; const char *label; nid = spec->dmic_nids[i]; @@ -3480,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (index < 0) continue; - if (active_mics == 1) - label = "Digital Mic"; - else - label = stac92xx_dmic_labels[dimux->num_items]; + label = hda_get_input_pin_label(codec, nid, 1); + snd_hda_add_imux_item(dimux, label, index, &type_idx); - err = create_elem_capture_vol(codec, nid, label, HDA_INPUT); + err = create_elem_capture_vol(codec, nid, label, type_idx, + HDA_INPUT); if (err < 0) return err; if (!err) { err = create_elem_capture_vol(codec, nid, label, - HDA_OUTPUT); + type_idx, HDA_OUTPUT); if (err < 0) return err; } - dimux->items[dimux->num_items].label = label; - dimux->items[dimux->num_items].index = index; - dimux->num_items++; if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) { - imux->items[imux->num_items].label = label; - imux->items[imux->num_items].index = index; - imux->num_items++; + snd_hda_add_imux_item(imux, label, index, NULL); + spec->num_analog_muxes++; } } @@ -3509,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, } static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *fixed, hda_nid_t *ext) + hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock) { unsigned int cfg; if (!nid) return 0; cfg = snd_hda_codec_get_pincfg(codec, nid); - switch (get_defcfg_connect(cfg)) { - case AC_JACK_PORT_FIXED: + switch (snd_hda_get_input_pin_attr(cfg)) { + case INPUT_PIN_ATTR_INT: if (*fixed) return 1; /* already occupied */ *fixed = nid; break; - case AC_JACK_PORT_COMPLEX: + case INPUT_PIN_ATTR_UNUSED: + break; + case INPUT_PIN_ATTR_DOCK: + if (*dock) + return 1; /* already occupied */ + *dock = nid; + break; + default: if (*ext) return 1; /* already occupied */ *ext = nid; @@ -3540,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec, int i; mic->pin = pin; - for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) - if (pin == cfg->input_pins[i]) + if (pin == 0) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { + if (pin == cfg->inputs[i].pin) break; - if (i <= AUTO_PIN_FRONT_MIC) { + } + if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) { /* analog pin */ i = get_connection_index(codec, spec->mux_nids[0], pin); if (i < 0) @@ -3574,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext; + hda_nid_t fixed, ext, dock; int i; - for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) { - if (cfg->input_pins[i]) + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN) return 0; /* must be exclusively mics */ } - fixed = ext = 0; - for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) - if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext)) + fixed = ext = dock = 0; + for (i = 0; i < cfg->num_inputs; i++) + if (check_mic_pin(codec, cfg->inputs[i].pin, + &fixed, &ext, &dock)) return 0; for (i = 0; i < spec->num_dmics; i++) - if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext)) + if (check_mic_pin(codec, spec->dmic_nids[i], + &fixed, &ext, &dock)) return 0; - if (!fixed || !ext) - return 0; + if (!fixed && !ext && !dock) + return 0; /* no input to switch */ if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) return 0; /* no unsol support */ if (set_mic_route(codec, &spec->ext_mic, ext) || - set_mic_route(codec, &spec->int_mic, fixed)) + set_mic_route(codec, &spec->int_mic, fixed) || + set_mic_route(codec, &spec->dock_mic, dock)) return 0; /* something is wrong */ return 1; } @@ -3604,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux; int i, j; + const char *label; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = cfg->input_pins[i]; - int index, err; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int index, err, type_idx; - if (!nid) - continue; index = -1; for (j = 0; j < spec->num_muxes; j++) { index = get_connection_index(codec, spec->mux_nids[j], @@ -3621,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const if (index < 0) continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, index, &type_idx); + err = create_elem_capture_vol(codec, nid, - auto_pin_cfg_labels[i], + label, type_idx, HDA_INPUT); if (err < 0) return err; - - imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = index; - imux->num_items++; } spec->num_analog_muxes = imux->num_items; @@ -4303,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec) AC_VERB_SET_CONNECT_SEL, 0); if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) stac_issue_unsol_event(codec, spec->ext_mic.pin); - } - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = cfg->input_pins[i]; - if (nid) { - unsigned int pinctl, conf; - if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { - /* for mic pins, force to initialize */ - pinctl = stac92xx_get_default_vref(codec, nid); + if (enable_pin_detect(codec, spec->dock_mic.pin, + STAC_MIC_EVENT)) + stac_issue_unsol_event(codec, spec->dock_mic.pin); + } + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; + unsigned int pinctl, conf; + if (type == AUTO_PIN_MIC) { + /* for mic pins, force to initialize */ + pinctl = stac92xx_get_default_vref(codec, nid); + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ + /* Also, if both INPUT and OUTPUT are set, + * it must be a BIOS bug; need to override, too + */ + if (!(pinctl & AC_PINCTL_IN_EN) || + (pinctl & AC_PINCTL_OUT_EN)) { + pinctl &= ~AC_PINCTL_OUT_EN; pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); - } else { - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* if PINCTL already set then skip */ - /* Also, if both INPUT and OUTPUT are set, - * it must be a BIOS bug; need to override, too - */ - if (!(pinctl & AC_PINCTL_IN_EN) || - (pinctl & AC_PINCTL_OUT_EN)) { - pinctl &= ~AC_PINCTL_OUT_EN; - pinctl |= AC_PINCTL_IN_EN; - stac92xx_auto_set_pinctl(codec, nid, - pinctl); - } - } - conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { - if (enable_pin_detect(codec, nid, - STAC_INSERT_EVENT)) - stac_issue_unsol_event(codec, nid); } } + conf = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { + if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT)) + stac_issue_unsol_event(codec, nid); + } } for (i = 0; i < spec->num_dmics; i++) stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], @@ -4381,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec) stac_issue_unsol_event(codec, nid); } -#ifdef CONFIG_SND_HDA_POWER_SAVE /* sync mute LED */ - if (spec->gpio_led && codec->patch_ops.check_power_status) - codec->patch_ops.check_power_status(codec, 0x01); -#endif + if (spec->gpio_led) + hda_call_check_power_status(codec, 0x01); if (spec->dac_list) stac92xx_power_down(codec); return 0; @@ -4686,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) } } +/* get the pin connection (fixed, none, etc) */ +static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int cfg; + + cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); + return get_defcfg_connect(cfg); +} + +static int stac92xx_connected_ports(struct hda_codec *codec, + hda_nid_t *nids, int num_nids) +{ + struct sigmatel_spec *spec = codec->spec; + int idx, num; + unsigned int def_conf; + + for (num = 0; num < num_nids; num++) { + for (idx = 0; idx < spec->num_pins; idx++) + if (spec->pin_nids[idx] == nids[num]) + break; + if (idx >= spec->num_pins) + break; + def_conf = stac_get_defcfg_connect(codec, idx); + if (def_conf == AC_JACK_PORT_NONE) + break; + } + return num; +} + static void stac92xx_mic_detect(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4693,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec) if (get_pin_presence(codec, spec->ext_mic.pin)) mic = &spec->ext_mic; + else if (get_pin_presence(codec, spec->dock_mic.pin)) + mic = &spec->dock_mic; else mic = &spec->int_mic; if (mic->dmux_idx >= 0) @@ -4935,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec) stac_issue_unsol_event(codec, spec->autocfg.line_out_pins[0]); } -#ifdef CONFIG_SND_HDA_POWER_SAVE /* sync mute LED */ - if (spec->gpio_led && codec->patch_ops.check_power_status) - codec->patch_ops.check_power_status(codec, 0x01); -#endif + if (spec->gpio_led) + hda_call_check_power_status(codec, 0x01); return 0; } @@ -5300,6 +5326,82 @@ again: return 0; } +static int stac92hd83xxx_set_system_btl_amp(struct hda_codec *codec) +{ + if (codec->vendor_id != 0x111d7605 && + codec->vendor_id != 0x111d76d1) + return 0; + + switch (codec->subsystem_id) { + case 0x103c1618: + case 0x103c1619: + case 0x103c161a: + case 0x103c161b: + case 0x103c161c: + case 0x103c161d: + case 0x103c161e: + case 0x103c161f: + case 0x103c1620: + case 0x103c1621: + case 0x103c1622: + case 0x103c1623: + + case 0x103c162a: + case 0x103c162b: + + case 0x103c1630: + case 0x103c1631: + + case 0x103c1633: + + case 0x103c1635: + + case 0x103c164f: + + case 0x103c1676: + case 0x103c1677: + case 0x103c1678: + case 0x103c1679: + case 0x103c167a: + case 0x103c167b: + case 0x103c167c: + case 0x103c167d: + case 0x103c167e: + case 0x103c167f: + case 0x103c1680: + case 0x103c1681: + case 0x103c1682: + case 0x103c1683: + case 0x103c1684: + case 0x103c1685: + case 0x103c1686: + case 0x103c1687: + case 0x103c1688: + case 0x103c1689: + case 0x103c168a: + case 0x103c168b: + case 0x103c168c: + case 0x103c168d: + case 0x103c168e: + case 0x103c168f: + case 0x103c1690: + case 0x103c1691: + case 0x103c1692: + + case 0x103c3587: + case 0x103c3588: + case 0x103c3589: + case 0x103c358a: + + case 0x103c3667: + case 0x103c3668: + /* set BTL amp level to 13.43dB for louder speaker output */ + return snd_hda_codec_write_cache(codec, codec->afg, 0, + 0x7F4, 0x14); + } + return 0; +} + static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5311,11 +5413,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + /* reset pin power-down; Windows may leave these bits after reboot */ + snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0); + snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0); codec->no_trigger_sense = 1; codec->spec = spec; spec->linear_tone_beep = 1; codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; spec->digbeep_nid = 0x21; + spec->dmic_nids = stac92hd83xxx_dmic_nids; + spec->dmux_nids = stac92hd83xxx_mux_nids; spec->mux_nids = stac92hd83xxx_mux_nids; spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids); spec->adc_nids = stac92hd83xxx_adc_nids; @@ -5361,9 +5468,13 @@ again: case 0x111d76d4: case 0x111d7605: case 0x111d76d5: + case 0x111d76e7: if (spec->board_config == STAC_92HD83XXX_PWR_REF) break; spec->num_pwrs = 0; + spec->num_dmics = stac92xx_connected_ports(codec, + stac92hd83xxx_dmic_nids, + STAC92HD83XXX_NUM_DMICS); break; } @@ -5417,41 +5528,13 @@ again: AC_VERB_SET_CONNECT_SEL, num_dacs); } + stac92hd83xxx_set_system_btl_amp(codec); + codec->proc_widget_hook = stac92hd_proc_hook; return 0; } -/* get the pin connection (fixed, none, etc) */ -static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int cfg; - - cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); - return get_defcfg_connect(cfg); -} - -static int stac92hd71bxx_connected_ports(struct hda_codec *codec, - hda_nid_t *nids, int num_nids) -{ - struct sigmatel_spec *spec = codec->spec; - int idx, num; - unsigned int def_conf; - - for (num = 0; num < num_nids; num++) { - for (idx = 0; idx < spec->num_pins; idx++) - if (spec->pin_nids[idx] == nids[num]) - break; - if (idx >= spec->num_pins) - break; - def_conf = stac_get_defcfg_connect(codec, idx); - if (def_conf == AC_JACK_PORT_NONE) - break; - } - return num; -} - static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, hda_nid_t dig0pin) { @@ -5590,7 +5673,7 @@ again: case 0x111d76b5: spec->init = stac92hd71bxx_core_init; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92hd71bxx_connected_ports(codec, + spec->num_dmics = stac92xx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS); break; @@ -5622,7 +5705,7 @@ again: snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0; - spec->num_dmics = stac92hd71bxx_connected_ports(codec, + spec->num_dmics = stac92xx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS - 1); break; @@ -5636,7 +5719,7 @@ again: default: spec->init = stac92hd71bxx_core_init; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92hd71bxx_connected_ports(codec, + spec->num_dmics = stac92xx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS); break; @@ -6303,6 +6386,23 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx }, + { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx }, + { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx }, + { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx }, + { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx }, + { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx }, + { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx }, + { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx}, + { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx}, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ae3acb2b42d1..d1c3f8defc48 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = { /* add dynamic controls */ -static int via_add_control(struct via_spec *spec, int type, const char *name, - unsigned long val) +static int __via_add_control(struct via_spec *spec, int type, const char *name, + int idx, unsigned long val) { struct snd_kcontrol_new *knew; @@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, return 0; } +#define via_add_control(spec, type, name, val) \ + __via_add_control(spec, type, name, 0, val) + static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, struct snd_kcontrol_new *tmpl) { @@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec) /* create input playback/capture controls for the given pin */ static int via_new_analog_input(struct via_spec *spec, const char *ctlname, - int idx, int mix_nid) + int type_idx, int idx, int mix_nid) { char name[32]; int err; sprintf(name, "%s Playback Volume", ctlname); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); - err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, + err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); static void via_auto_init_analog_input(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int ctl; int i; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; - if (!nid) - continue; - + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; if (spec->smart51_enabled && is_smart51_pins(spec, nid)) ctl = PIN_OUT; - else if (i <= AUTO_PIN_FRONT_MIC) + else if (i == AUTO_PIN_MIC) ctl = PIN_VREF50; else ctl = PIN_IN; @@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute) } static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) { - int res = 0; - int index; - for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { - if (pin == spec->autocfg.input_pins[index]) { - res = 1; - break; - } + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (pin == cfg->inputs[i].pin) + return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; } - return res; + return 0; } static int via_smart51_info(struct snd_kcontrol *kcontrol, @@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + const struct auto_pin_cfg *cfg = &spec->autocfg; int on = 1; int i; - for (i = 0; i < ARRAY_SIZE(index); i++) { - hda_nid_t nid = spec->autocfg.input_pins[index[i]]; - if (nid) { - int ctl = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0); - if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode - && spec->codec_type != VT1718S) - continue; /* ignore FMic for independent HP */ - if (ctl & AC_PINCTL_IN_EN - && !(ctl & AC_PINCTL_OUT_EN)) - on = 0; - } + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + continue; + if (cfg->inputs[i].type == AUTO_PIN_MIC && + spec->hp_independent_mode && spec->codec_type != VT1718S) + continue; /* ignore FMic for independent HP */ + if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) + on = 0; } *ucontrol->value.integer.value = on; return 0; @@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; int out_in = *ucontrol->value.integer.value ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; - int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; int i; - for (i = 0; i < ARRAY_SIZE(index); i++) { - hda_nid_t nid = spec->autocfg.input_pins[index[i]]; - if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode - && spec->codec_type != VT1718S) + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + unsigned int parm; + + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + continue; + if (cfg->inputs[i].type == AUTO_PIN_MIC && + spec->hp_independent_mode && spec->codec_type != VT1718S) continue; /* don't retask FMic for independent HP */ - if (nid) { - unsigned int parm = snd_hda_codec_read( - codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - parm |= out_in; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - parm); - if (out_in == AC_PINCTL_OUT_EN) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - if (spec->codec_type == VT1718S) - snd_hda_codec_amp_stereo( + + parm = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + if (spec->codec_type == VT1718S) { + snd_hda_codec_amp_stereo( codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); } - if (i == AUTO_PIN_FRONT_MIC) { + if (cfg->inputs[i].type == AUTO_PIN_MIC) { if (spec->codec_type == VT1708S || spec->codec_type == VT1716S) { /* input = index 1 (AOW3) */ @@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = { static int via_smart51_build(struct via_spec *spec) { struct snd_kcontrol_new *knew; - int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; int i; @@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec) if (knew == NULL) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(index); i++) { - nid = spec->autocfg.input_pins[index[i]]; - if (nid) { + for (i = 0; i < cfg->num_inputs; i++) { + nid = cfg->inputs[i].pin; + if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { knew = via_clone_control(spec, &via_smart51_mixer[1]); if (knew == NULL) return -ENOMEM; knew->subdevice = nid; + break; } } @@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec) static const char *texts[] = { "OFF", "ON", NULL}; /* for hp mode select */ - i = 0; - while (texts[i] != NULL) { - imux->items[imux->num_items].label = texts[i]; - imux->items[imux->num_items].index = i; - imux->num_items++; - i++; - } + for (i = 0; texts[i]; i++) + snd_hda_add_imux_item(imux, texts[i], i, NULL); spec->hp_mux = &spec->private_imux[1]; } @@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + hda_nid_t cap_nid, + hda_nid_t pin_idxs[], int num_idxs) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; + struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; + int i, err, idx, type, type_idx = 0; /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = idx; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x1d: /* Mic */ - idx = 2; - break; - - case 0x1e: /* Line In */ - idx = 3; - break; - - case 0x21: /* Front Mic */ - idx = 4; - break; - - case 0x24: /* CD */ - idx = 1; + for (idx = 0; idx < num_idxs; idx++) { + if (pin_idxs[idx] == 0xff) { + snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); break; } - err = via_new_analog_input(spec, labels[i], idx, 0x17); + } + + for (i = 0; i < cfg->num_inputs; i++) { + const char *label; + type = cfg->inputs[i].type; + for (idx = 0; idx < num_idxs; idx++) + if (pin_idxs[idx] == cfg->inputs[i].pin) + break; + if (idx >= num_idxs) + continue; + if (i > 0 && type == cfg->inputs[i - 1].type) + type_idx++; + else + type_idx = 0; + label = hda_get_autocfg_input_label(codec, cfg, i); + err = via_new_analog_input(spec, label, type_idx, idx, cap_nid); if (err < 0) return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; + snd_hda_add_imux_item(imux, label, idx, NULL); } return 0; } +/* create playback/capture controls for input pins */ +static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, + ARRAY_SIZE(pin_idxs)); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE static struct hda_amp_list vt1708_loopbacks[] = { { 0x17, HDA_INPUT, 1 }, @@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; /* add jack detect on/off control */ @@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = idx; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x1d: /* Mic */ - idx = 2; - break; - - case 0x1e: /* Line In */ - idx = 3; - break; - - case 0x21: /* Front Mic */ - idx = 4; - break; - - case 0x23: /* CD */ - idx = 1; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x18); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, + ARRAY_SIZE(pin_idxs)); } static int vt1709_parse_auto_config(struct hda_codec *codec) @@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = idx; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x1a: /* Mic */ - idx = 2; - break; - - case 0x1b: /* Line In */ - idx = 3; - break; - - case 0x1e: /* Front Mic */ - idx = 4; - break; - - case 0x1f: /* CD */ - idx = 1; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x16); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, + ARRAY_SIZE(pin_idxs)); } static int vt1708B_parse_auto_config(struct hda_codec *codec) @@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 5; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x1a: /* Mic */ - idx = 2; - break; - - case 0x1b: /* Line In */ - idx = 3; - break; - - case 0x1e: /* Front Mic */ - idx = 4; - break; - - case 0x1f: /* CD */ - idx = 1; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x16); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx-1; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, + ARRAY_SIZE(pin_idxs)); } /* fill out digital output widgets; one for master and one for slave outputs */ @@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) imux = &spec->private_imux[1]; /* for hp mode select */ - i = 0; - while (texts[i] != NULL) { - imux->items[imux->num_items].label = texts[i]; - imux->items[imux->num_items].index = i; - imux->num_items++; - i++; - } + for (i = 0; texts[i]; i++) + snd_hda_add_imux_item(imux, texts[i], i, NULL); spec->hp_mux = &spec->private_imux[1]; return 0; } /* create playback/capture controls for input pins */ -static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 3; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x14: /* Mic */ - idx = 1; - break; - - case 0x15: /* Line In */ - idx = 2; - break; - - case 0x18: /* Front Mic */ - idx = 3; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x1A); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx-1; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, + ARRAY_SIZE(pin_idxs)); } static int vt1702_parse_auto_config(struct hda_codec *codec) @@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | (1 << AC_AMPCAP_MUTE_SHIFT)); - err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 5; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x2b: /* Mic */ - idx = 1; - break; - - case 0x2a: /* Line In */ - idx = 2; - break; - - case 0x29: /* Front Mic */ - idx = 3; - break; - - case 0x2c: /* CD */ - idx = 0; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x21); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, + ARRAY_SIZE(pin_idxs)); } static int vt1718S_parse_auto_config(struct hda_codec *codec) @@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 5; - imux->num_items++; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x1a: /* Mic */ - idx = 2; - break; - - case 0x1b: /* Line In */ - idx = 3; - break; - - case 0x1e: /* Front Mic */ - idx = 4; - break; - - case 0x1f: /* CD */ - idx = 1; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x16); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx-1; - imux->num_items++; - } - return 0; + static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; + return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, + ARRAY_SIZE(pin_idxs)); } static int vt1716S_parse_auto_config(struct hda_codec *codec) @@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; + struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x2b: /* Mic */ - idx = 0; - break; - - case 0x2a: /* Line In */ - idx = 1; - break; - - case 0x29: /* Front Mic */ - idx = 2; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x21); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } + static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; + int err; + err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, + ARRAY_SIZE(pin_idxs)); + if (err < 0) + return err; /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); + err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); if (err < 0) return err; - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 3; - imux->num_items++; - /* for digital mic select */ - imux->items[imux->num_items].label = "Digital Mic"; - imux->items[imux->num_items].index = 4; - imux->num_items++; + snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); return 0; } @@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) } /* create playback/capture controls for input pins */ -static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, +static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - static char *labels[] = { - "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL - }; + struct via_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx = 0; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!cfg->input_pins[i]) - continue; - - switch (cfg->input_pins[i]) { - case 0x2b: /* Mic */ - idx = 0; - break; + static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; + int err; - case 0x2a: /* Line In */ - idx = 1; - break; + err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, + ARRAY_SIZE(pin_idxs)); + if (err < 0) + return err; - case 0x29: /* Front Mic */ - idx = 2; - break; - } - err = via_new_analog_input(spec, labels[i], idx, 0x21); - if (err < 0) - return err; - imux->items[imux->num_items].label = labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); + err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); if (err < 0) return err; - /* for internal loopback recording select */ - imux->items[imux->num_items].label = "Stereo Mixer"; - imux->items[imux->num_items].index = 5; - imux->num_items++; - /* for digital mic select */ - imux->items[imux->num_items].label = "Digital Mic"; - imux->items[imux->num_items].index = 6; - imux->num_items++; + snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); return 0; } @@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index d216362626d0..712c1710f9a2 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -563,6 +563,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_MEDIASTATION: + case ICE1712_SUBDEVICE_EDIROLDA2496: ice->num_total_dacs = 8; ice->num_total_adcs = 8; break; @@ -635,6 +636,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice); break; case ICE1712_SUBDEVICE_DELTA1010LT: + case ICE1712_SUBDEVICE_EDIROLDA2496: err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice); break; case ICE1712_SUBDEVICE_DELTA66: @@ -734,6 +736,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA66: case ICE1712_SUBDEVICE_VX442: case ICE1712_SUBDEVICE_DELTA66E: + case ICE1712_SUBDEVICE_EDIROLDA2496: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; @@ -813,5 +816,12 @@ struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { .chip_init = snd_ice1712_delta_init, .build_controls = snd_ice1712_delta_add_controls, }, + { + .subvendor = ICE1712_SUBDEVICE_EDIROLDA2496, + .name = "Edirol DA2496", + .model = "da2496", + .chip_init = snd_ice1712_delta_init, + .build_controls = snd_ice1712_delta_add_controls, + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index f7f14df81f26..1a0ac6cd6501 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -34,7 +34,8 @@ "{MidiMan M Audio,Delta 410},"\ "{MidiMan M Audio,Audiophile 24/96},"\ "{Digigram,VX442},"\ - "{Lionstracs,Mediastation}," + "{Lionstracs,Mediastation},"\ + "{Edirol,DA2496}," #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 #define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 @@ -47,6 +48,7 @@ #define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 #define ICE1712_SUBDEVICE_VX442 0x12143cd6 #define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100 +#define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010 /* entry point */ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 6bc3f91b7281..cdb873f5da50 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -638,7 +638,7 @@ static struct snd_kcontrol_new pontis_controls[] __devinitdata = { */ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; char line[64]; unsigned int reg, val; mutex_lock(&ice->gpio_mutex); @@ -653,7 +653,7 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; mutex_lock(&ice->gpio_mutex); @@ -676,7 +676,7 @@ static void wm_proc_init(struct snd_ice1712 *ice) static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; mutex_lock(&ice->gpio_mutex); diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 2a8e5cd8f2d8..e36ddb94c382 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -654,7 +654,7 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) static void stac9460_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; /* registers 0x0 - 0x14 */ for (reg = 0; reg <= 0x15; reg++) { diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6433e65c9507..467749249576 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1776,6 +1776,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { }, { .subvendor = 0x1014, + .subdevice = 0x0534, + .name = "ThinkPad X31", + .type = AC97_TUNE_INV_EAPD + }, + { + .subvendor = 0x1014, .subdevice = 0x1f00, .name = "MS-9128", .type = AC97_TUNE_ALC_JACK diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 289cb4dacfc7..98a8eb3c92f7 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, @@ -543,6 +545,10 @@ static int __devinit get_oxygen_model(struct oxygen *chip, chip->model.suspend = claro_suspend; chip->model.resume = claro_resume; chip->model.set_adc_params = set_ak5385_params; + chip->model.device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF; break; } if (id->driver_data == MODEL_MERIDIAN || diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 6147216af744..7d5222caa0a9 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, @@ -155,6 +156,7 @@ void oxygen_pci_remove(struct pci_dev *pci); int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state); int oxygen_pci_resume(struct pci_dev *pci); #endif +void oxygen_pci_shutdown(struct pci_dev *pci); /* oxygen_mixer.c */ diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index fad03d64e3ad..e5ebe56fb0c5 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; } } @@ -519,16 +540,21 @@ static void oxygen_init(struct oxygen *chip) } } -static void oxygen_card_free(struct snd_card *card) +static void oxygen_shutdown(struct oxygen *chip) { - struct oxygen *chip = card->private_data; - spin_lock_irq(&chip->reg_lock); chip->interrupt_mask = 0; chip->pcm_running = 0; oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); spin_unlock_irq(&chip->reg_lock); +} + +static void oxygen_card_free(struct snd_card *card) +{ + struct oxygen *chip = card->private_data; + + oxygen_shutdown(chip); if (chip->irq >= 0) free_irq(chip->irq, chip); flush_scheduled_work(); @@ -608,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); @@ -778,3 +804,13 @@ int oxygen_pci_resume(struct pci_dev *pci) } EXPORT_SYMBOL(oxygen_pci_resume); #endif /* CONFIG_PM */ + +void oxygen_pci_shutdown(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct oxygen *chip = card->private_data; + + oxygen_shutdown(chip); + chip->model.cleanup(chip); +} +EXPORT_SYMBOL(oxygen_pci_shutdown); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index f375b8a27862..2849b36f5f7e 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 9dff6954c397..814667442eb0 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 72de159d4567..4dcd41b78258 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 f03a2f2cffee..469010a8b849 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 }, { } @@ -95,6 +96,7 @@ static struct pci_driver xonar_driver = { .suspend = oxygen_pci_suspend, .resume = oxygen_pci_resume, #endif + .shutdown = oxygen_pci_shutdown, }; static int __init alsa_card_xonar_init(void) diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 7c4986b27f2b..aa27c31049af 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 ba18fb546b4f..d491fd6c0be2 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 dbc4b89d74e4..200f7601276f 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 @@ -53,7 +62,10 @@ struct xonar_wm87x6 { struct xonar_generic generic; u16 wm8776_regs[0x17]; u16 wm8766_regs[0x10]; + 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, @@ -95,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, @@ -105,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) @@ -139,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)); @@ -168,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; @@ -176,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"); } @@ -193,6 +248,7 @@ static void xonar_ds_init(struct oxygen *chip) static void xonar_ds_cleanup(struct oxygen *chip) { xonar_disable_output(chip); + wm8776_write(chip, WM8776_RESET, 0); } static void xonar_ds_suspend(struct oxygen *chip) @@ -205,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, @@ -320,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, @@ -603,6 +675,7 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl, { struct oxygen *chip = ctl->private_data; struct xonar_wm87x6 *data = chip->model_data; + struct snd_kcontrol *other_ctl; unsigned int mux_bit = ctl->private_value; u16 reg; int changed; @@ -610,8 +683,18 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl, mutex_lock(&chip->mutex); reg = data->wm8776_regs[WM8776_ADCMUX]; if (value->value.integer.value[0]) { - reg &= ~0x003; reg |= mux_bit; + /* line-in and mic-in are exclusive */ + mux_bit ^= 3; + if (reg & mux_bit) { + reg &= ~mux_bit; + if (mux_bit == 1) + other_ctl = data->line_adcmux_control; + else + other_ctl = data->mic_adcmux_control; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &other_ctl->id); + } } else reg &= ~mux_bit; changed = reg != data->wm8776_regs[WM8776_ADCMUX]; @@ -882,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", @@ -942,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; @@ -963,7 +1042,13 @@ static int xonar_ds_mixer_init(struct oxygen *chip) err = snd_ctl_add(chip->card, ctl); if (err < 0) return err; + if (!strcmp(ctl->id.name, "Line Capture Switch")) + data->line_adcmux_control = ctl; + else if (!strcmp(ctl->id.name, "Mic Capture Switch")) + data->mic_adcmux_control = ctl; } + if (!data->line_adcmux_control || !data->mic_adcmux_control) + return -ENXIO; BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { ctl = snd_ctl_new1(&lc_controls[i], chip); @@ -979,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, @@ -993,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/riptide/riptide.c b/sound/pci/riptide/riptide.c index f64fb7d988cb..ad5202efd7a9 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1224,15 +1224,14 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) firmware.firmware.ASIC, firmware.firmware.CODEC, firmware.firmware.AUXDSP, firmware.firmware.PROG); + if (!chip) + return 1; + for (i = 0; i < FIRMWARE_VERSIONS; i++) { if (!memcmp(&firmware_versions[i], &firmware, sizeof(firmware))) - break; - } - if (i >= FIRMWARE_VERSIONS) - return 0; /* no match */ + return 1; /* OK */ - if (!chip) - return 1; /* OK */ + } snd_printdd("Writing Firmware\n"); if (!chip->fw_entry) { diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index d19dc052c391..d5f5b440fc40 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1527,14 +1527,14 @@ snd_rme96_free(void *private_data) static void snd_rme96_free_spdif_pcm(struct snd_pcm *pcm) { - struct rme96 *rme96 = (struct rme96 *) pcm->private_data; + struct rme96 *rme96 = pcm->private_data; rme96->spdif_pcm = NULL; } static void snd_rme96_free_adat_pcm(struct snd_pcm *pcm) { - struct rme96 *rme96 = (struct rme96 *) pcm->private_data; + struct rme96 *rme96 = pcm->private_data; rme96->adat_pcm = NULL; } @@ -1661,7 +1661,7 @@ static void snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { int n; - struct rme96 *rme96 = (struct rme96 *)entry->private_data; + struct rme96 *rme96 = entry->private_data; rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); @@ -2348,7 +2348,7 @@ snd_rme96_probe(struct pci_dev *pci, if (err < 0) return err; card->private_free = snd_rme96_card_free; - rme96 = (struct rme96 *)card->private_data; + rme96 = card->private_data; rme96->card = card; rme96->pci = pci; snd_card_set_dev(card, &pci->dev); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index b92adef8e81e..0b720cf7783e 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; @@ -4609,6 +4609,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne if (err < 0) return err; + memset(&info, 0, sizeof(info)); spin_lock_irqsave(&hdsp->lock, flags); info.pref_sync_ref = (unsigned char)hdsp_pref_sync_ref(hdsp); info.wordclock_sync_check = (unsigned char)hdsp_wc_sync_check(hdsp); @@ -5155,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); @@ -5181,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/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 547b713d7204..0c98ef9156d8 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4127,6 +4127,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: + memset(&info, 0, sizeof(info)); spin_lock_irq(&hdspm->lock); info.pref_sync_ref = hdspm_pref_sync_ref(hdspm); info.wordclock_sync_check = hdspm_wc_sync_check(hdspm); diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 2f12da4da561..581a670e8261 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -579,7 +579,7 @@ static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, rate * delay_ms / 1000) * substream->runtime->channels; - pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", + pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", __func__, delay_ms, rate, diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 20afdf9772ee..961d98297695 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/codecs/Kconfig b/sound/soc/codecs/Kconfig index 658cbe07fb72..8d11fbd1b1aa 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -26,8 +26,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C - select SND_SOC_JZ4740 if SOC_JZ4740 + select SND_SOC_JZ4740_CODEC if SOC_JZ4740 select SND_SOC_MAX98088 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index c53955fe17b6..de799cd1ba72 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -47,7 +47,7 @@ static int ad73311_probe(struct platform_device *pdev) &soc_codec_dev_ad73311, &ad73311_dai, 1); } -static int ad73311_remove(struct platform_device *pdev) +static int __devexit ad73311_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index e7a40d16df90..bc22ee93a75d 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -2051,7 +2051,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c, return ret; } -static int max98088_i2c_remove(struct i2c_client *client) +static int __devexit max98088_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); kfree(i2c_get_clientdata(client)); diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index bf6d01f4a45a..d251ff54a2d3 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -524,6 +524,22 @@ static const struct soc_enum dac33_fifo_mode_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts), dac33_fifo_mode_texts); +/* L/R Line Output Gain */ +static const char *lr_lineout_gain_texts[] = { + "Line -12dB DAC 0dB", "Line -6dB DAC 6dB", + "Line 0dB DAC 12dB", "Line 6dB DAC 18dB", +}; + +static const struct soc_enum l_lineout_gain_enum = + SOC_ENUM_SINGLE(DAC33_LDAC_PWR_CTRL, 0, + ARRAY_SIZE(lr_lineout_gain_texts), + lr_lineout_gain_texts); + +static const struct soc_enum r_lineout_gain_enum = + SOC_ENUM_SINGLE(DAC33_RDAC_PWR_CTRL, 0, + ARRAY_SIZE(lr_lineout_gain_texts), + lr_lineout_gain_texts); + /* * DACL/R digital volume control: * from 0 dB to -63.5 in 0.5 dB steps @@ -541,6 +557,8 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = { DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1), SOC_DOUBLE_R("Line to Line Out Volume", DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1), + SOC_ENUM("Left Line Output Gain", l_lineout_gain_enum), + SOC_ENUM("Right Line Output Gain", r_lineout_gain_enum), }; static const struct snd_kcontrol_new dac33_mode_snd_controls[] = { @@ -651,6 +669,7 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_codec *codec = dac33->codec; + unsigned int delay; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -666,8 +685,9 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); /* Enable Alarm Threshold IRQ with a delay */ - udelay(SAMPLES_TO_US(dac33->burst_rate, - dac33->alarm_threshold)); + delay = SAMPLES_TO_US(dac33->burst_rate, + dac33->alarm_threshold) + 1000; + usleep_range(delay, delay + 500); dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT); break; case DAC33_FIFO_MODE7: @@ -767,11 +787,11 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev) static void dac33_oscwait(struct snd_soc_codec *codec) { - int timeout = 20; + int timeout = 60; u8 reg; do { - msleep(1); + usleep_range(1000, 2000); dac33_read(codec, DAC33_INT_OSC_STATUS, ®); } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--); if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 99b70e5978a2..329acc1a2074 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -98,16 +98,21 @@ static u8 tpa6130a2_read(int reg) return data->regs[reg]; } -static void tpa6130a2_initialize(void) +static int tpa6130a2_initialize(void) { struct tpa6130a2_data *data; - int i; + int i, ret = 0; BUG_ON(tpa6130a2_client == NULL); data = i2c_get_clientdata(tpa6130a2_client); - for (i = 1; i < TPA6130A2_REG_VERSION; i++) - tpa6130a2_i2c_write(i, data->regs[i]); + for (i = 1; i < TPA6130A2_REG_VERSION; i++) { + ret = tpa6130a2_i2c_write(i, data->regs[i]); + if (ret < 0) + break; + } + + return ret; } static int tpa6130a2_power(int power) @@ -133,7 +138,16 @@ static int tpa6130a2_power(int power) } data->power_state = 1; - tpa6130a2_initialize(); + ret = tpa6130a2_initialize(); + if (ret < 0) { + dev_err(&tpa6130a2_client->dev, + "Failed to initialize chip\n"); + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + regulator_disable(data->supply); + data->power_state = 0; + goto exit; + } /* Clear SWS */ val = tpa6130a2_read(TPA6130A2_REG_CONTROL); @@ -375,7 +389,9 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec) { struct tpa6130a2_data *data; - BUG_ON(tpa6130a2_client == NULL); + if (tpa6130a2_client == NULL) + return -ENODEV; + data = i2c_get_clientdata(tpa6130a2_client); snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index b4f11724a63f..aca4b1ea10bb 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -186,7 +186,6 @@ static int wm8900_volatile_register(unsigned int reg) { switch (reg) { case WM8900_REG_ID: - case WM8900_REG_POWER1: return 1; default: return 0; @@ -1200,11 +1199,6 @@ static int wm8900_probe(struct snd_soc_codec *codec) return -ENODEV; } - /* Read back from the chip */ - reg = snd_soc_read(codec, WM8900_REG_POWER1); - reg = (reg >> 12) & 0xf; - dev_info(codec->dev, "WM8900 revision %d\n", reg); - wm8900_reset(codec); /* Turn the chip on */ diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 7a1825418ee4..99c046ba46bb 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -665,7 +665,7 @@ static int wm9090_i2c_probe(struct i2c_client *i2c, return ret; } -static int wm9090_i2c_remove(struct i2c_client *i2c) +static int __devexit wm9090_i2c_remove(struct i2c_client *i2c) { struct wm9090_priv *wm9090 = i2c_get_clientdata(i2c); diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 997c54f3693c..009b6521a1bf 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c @@ -152,7 +152,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/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index fe15bb26e484..25f27ec1dd6e 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c @@ -24,7 +24,6 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-of-simple.h> #include "mpc5200_dma.h" #include "mpc5200_psc_ac97.h" @@ -49,7 +48,7 @@ static struct snd_soc_dai_link pcm030_fabric_dai[] = { .codec_dai_name = "wm9712-aux", .cpu_dai_name = "mpc5200-psc-ac97.1", .platform_name = "mpc5200-pcm-audio", - ..codec_name = "wm9712-codec", + .codec_name = "wm9712-codec", }, }; diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c index 68bd902ccd4e..979dd508305f 100644 --- a/sound/soc/omap/am3517evm.c +++ b/sound/soc/omap/am3517evm.c @@ -157,10 +157,8 @@ static int __init am3517evm_soc_init(void) { int ret; - if (!machine_is_omap3517evm()) { - pr_err("Not OMAP3517 / AM3517 EVM!\n"); + if (!machine_is_omap3517evm()) return -ENODEV; - } pr_info("OMAP3517 / AM3517 EVM SoC init\n"); am3517evm_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c index d296cfcc672e..fd3a40f309c8 100644 --- a/sound/soc/omap/igep0020.c +++ b/sound/soc/omap/igep0020.c @@ -101,10 +101,8 @@ static int __init igep2_soc_init(void) { int ret; - if (!machine_is_igep0020()) { - pr_debug("Not IGEP v2!\n"); + if (!machine_is_igep0020()) return -ENODEV; - } printk(KERN_INFO "IGEP v2 SoC init\n"); igep2_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c index 38cd1894623e..cf3fc8a675b5 100644 --- a/sound/soc/omap/omap2evm.c +++ b/sound/soc/omap/omap2evm.c @@ -103,10 +103,8 @@ static int __init omap2evm_soc_init(void) { int ret; - if (!machine_is_omap2evm()) { - pr_debug("Not omap2evm!\n"); + if (!machine_is_omap2evm()) return -ENODEV; - } printk(KERN_INFO "omap2evm SoC init\n"); omap2evm_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index 7c11e1afe9e6..e56832b0c444 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c @@ -112,10 +112,8 @@ static int __init omap3beagle_soc_init(void) { int ret; - if (!(machine_is_omap3_beagle() || machine_is_devkit8000())) { - pr_debug("Not OMAP3 Beagle or Devkit8000!\n"); + if (!(machine_is_omap3_beagle() || machine_is_devkit8000())) return -ENODEV; - } pr_info("OMAP3 Beagle/Devkit8000 SoC init\n"); omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c index 1ac5babef00d..810f1e36da21 100644 --- a/sound/soc/omap/omap3evm.c +++ b/sound/soc/omap/omap3evm.c @@ -99,10 +99,8 @@ static int __init omap3evm_soc_init(void) { int ret; - if (!machine_is_omap3evm()) { - pr_err("Not OMAP3 EVM!\n"); + if (!machine_is_omap3evm()) return -ENODEV; - } pr_info("OMAP3 EVM SoC init\n"); omap3evm_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 76ce77b91844..07fbcf7d2411 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -296,10 +296,8 @@ static int __init sdp3430_soc_init(void) int ret; u8 pin_mux; - if (!machine_is_omap_3430sdp()) { - pr_debug("Not SDP3430!\n"); + if (!machine_is_omap_3430sdp()) return -ENODEV; - } printk(KERN_INFO "SDP3430 SoC init\n"); sdp3430_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 62f6a622d791..4b4463db6ba0 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -186,10 +186,8 @@ static int __init sdp4430_soc_init(void) { int ret; - if (!machine_is_omap_4430sdp()) { - pr_debug("Not SDP4430!\n"); + if (!machine_is_omap_4430sdp()) return -ENODEV; - } printk(KERN_INFO "SDP4430 SoC init\n"); sdp4430_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 338dc9552bd6..718031eeac34 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -245,10 +245,8 @@ static int __init zoom2_soc_init(void) { int ret; - if (!machine_is_omap_zoom2()) { - pr_debug("Not Zoom2!\n"); + if (!machine_is_omap_zoom2()) return -ENODEV; - } printk(KERN_INFO "Zoom2 SoC init\n"); zoom2_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index c457bfd8297c..e97bdf150a03 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 d7a39a0fe99b..f4f2ee731f01 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/soc/s3c24xx/s3c-dma.c b/sound/soc/s3c24xx/s3c-dma.c index 54bff83c98b4..243f79bf43bb 100644 --- a/sound/soc/s3c24xx/s3c-dma.c +++ b/sound/soc/s3c24xx/s3c-dma.c @@ -94,8 +94,7 @@ static void s3c_dma_enqueue(struct snd_pcm_substream *substream) if ((pos + len) > prtd->dma_end) { len = prtd->dma_end - pos; - pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n", - __func__, len); + pr_debug("%s: corrected dma len %ld\n", __func__, len); } ret = s3c2410_dma_enqueue(prtd->params->channel, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 70d9a7394b2b..805343fe903b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -165,8 +165,11 @@ static ssize_t pmdown_time_set(struct device *dev, { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); + int ret; - strict_strtol(buf, 10, &rtd->pmdown_time); + ret = strict_strtol(buf, 10, &rtd->pmdown_time); + if (ret) + return ret; return count; } diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index ff0b2a8fd25b..5ae1eae9f6db 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 44d6d2ec964f..112984f4080f 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -65,6 +65,7 @@ config SND_USB_CAIAQ * Native Instruments Guitar Rig Session I/O * Native Instruments Guitar Rig mobile * Native Instruments Traktor Kontrol X1 + * Native Instruments Traktor Kontrol S4 To compile this driver as a module, choose M here: the module will be called snd-usb-caiaq. @@ -82,6 +83,7 @@ config SND_USB_CAIAQ_INPUT * Native Instruments Kore Controller * Native Instruments Kore Controller 2 * Native Instruments Audio Kontrol 1 + * Native Instruments Traktor Kontrol S4 config SND_USB_US122L tristate "Tascam US-122L USB driver" diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 4328cad6c3a2..68b97477577b 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -111,7 +111,7 @@ static int stream_start(struct snd_usb_caiaqdev *dev) memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); dev->input_panic = 0; dev->output_panic = 0; - dev->first_packet = 1; + dev->first_packet = 4; dev->streaming = 1; dev->warned = 0; @@ -169,7 +169,7 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) } static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, - struct snd_pcm_hw_params *hw_params) + struct snd_pcm_hw_params *hw_params) { debug("%s(%p)\n", __func__, sub); return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params)); @@ -189,7 +189,7 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) #endif static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; + 48000, 64000, 88200, 96000, 176400, 192000 }; static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) { @@ -201,12 +201,39 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) debug("%s(%p)\n", __func__, substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev->period_out_count[index] = BYTES_PER_SAMPLE + 1; - dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; + int out_pos; + + switch (dev->spec.data_alignment) { + case 0: + case 2: + out_pos = BYTES_PER_SAMPLE + 1; + break; + case 3: + default: + out_pos = 0; + break; + } + + dev->period_out_count[index] = out_pos; + dev->audio_out_buf_pos[index] = out_pos; } else { - int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2; - dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos; - dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos; + int in_pos; + + switch (dev->spec.data_alignment) { + case 0: + in_pos = BYTES_PER_SAMPLE + 2; + break; + case 2: + in_pos = BYTES_PER_SAMPLE; + break; + case 3: + default: + in_pos = 0; + break; + } + + dev->period_in_count[index] = in_pos; + dev->audio_in_buf_pos[index] = in_pos; } if (dev->streaming) @@ -221,7 +248,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) snd_pcm_limit_hw_rates(runtime); bytes_per_sample = BYTES_PER_SAMPLE; - if (dev->spec.data_alignment == 2) + if (dev->spec.data_alignment >= 2) bytes_per_sample++; bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE) @@ -253,6 +280,8 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) { struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); + debug("%s(%p) cmd %d\n", __func__, sub, cmd); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -402,6 +431,61 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, } } +static void read_in_urb_mode3(struct snd_usb_caiaqdev *dev, + const struct urb *urb, + const struct usb_iso_packet_descriptor *iso) +{ + unsigned char *usb_buf = urb->transfer_buffer + iso->offset; + int stream, i; + + /* paranoia check */ + if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM)) + return; + + for (i = 0; i < iso->actual_length;) { + for (stream = 0; stream < dev->n_streams; stream++) { + struct snd_pcm_substream *sub = dev->sub_capture[stream]; + char *audio_buf = NULL; + int c, n, sz = 0; + + if (sub && !dev->input_panic) { + struct snd_pcm_runtime *rt = sub->runtime; + audio_buf = rt->dma_area; + sz = frames_to_bytes(rt, rt->buffer_size); + } + + for (c = 0; c < CHANNELS_PER_STREAM; c++) { + /* 3 audio data bytes, followed by 1 check byte */ + if (audio_buf) { + for (n = 0; n < BYTES_PER_SAMPLE; n++) { + audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n]; + + if (dev->audio_in_buf_pos[stream] == sz) + dev->audio_in_buf_pos[stream] = 0; + } + + dev->period_in_count[stream] += BYTES_PER_SAMPLE; + } + + i += BYTES_PER_SAMPLE; + + if (usb_buf[i] != ((stream << 1) | c) && + !dev->first_packet) { + if (!dev->input_panic) + printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n", + ((stream << 1) | c), usb_buf[i], c, stream, i); + dev->input_panic = 1; + } + + i++; + } + } + } + + if (dev->first_packet > 0) + dev->first_packet--; +} + static void read_in_urb(struct snd_usb_caiaqdev *dev, const struct urb *urb, const struct usb_iso_packet_descriptor *iso) @@ -419,6 +503,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev, case 2: read_in_urb_mode2(dev, urb, iso); break; + case 3: + read_in_urb_mode3(dev, urb, iso); + break; } if ((dev->input_panic || dev->output_panic) && !dev->warned) { @@ -429,9 +516,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev, } } -static void fill_out_urb(struct snd_usb_caiaqdev *dev, - struct urb *urb, - const struct usb_iso_packet_descriptor *iso) +static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *dev, + struct urb *urb, + const struct usb_iso_packet_descriptor *iso) { unsigned char *usb_buf = urb->transfer_buffer + iso->offset; struct snd_pcm_substream *sub; @@ -457,9 +544,67 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, /* fill in the check bytes */ if (dev->spec.data_alignment == 2 && i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == - (dev->n_streams * CHANNELS_PER_STREAM)) - for (stream = 0; stream < dev->n_streams; stream++, i++) - usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i); + (dev->n_streams * CHANNELS_PER_STREAM)) + for (stream = 0; stream < dev->n_streams; stream++, i++) + usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i); + } +} + +static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev, + struct urb *urb, + const struct usb_iso_packet_descriptor *iso) +{ + unsigned char *usb_buf = urb->transfer_buffer + iso->offset; + int stream, i; + + for (i = 0; i < iso->length;) { + for (stream = 0; stream < dev->n_streams; stream++) { + struct snd_pcm_substream *sub = dev->sub_playback[stream]; + char *audio_buf = NULL; + int c, n, sz = 0; + + if (sub) { + struct snd_pcm_runtime *rt = sub->runtime; + audio_buf = rt->dma_area; + sz = frames_to_bytes(rt, rt->buffer_size); + } + + for (c = 0; c < CHANNELS_PER_STREAM; c++) { + for (n = 0; n < BYTES_PER_SAMPLE; n++) { + if (audio_buf) { + usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++]; + + if (dev->audio_out_buf_pos[stream] == sz) + dev->audio_out_buf_pos[stream] = 0; + } else { + usb_buf[i+n] = 0; + } + } + + if (audio_buf) + dev->period_out_count[stream] += BYTES_PER_SAMPLE; + + i += BYTES_PER_SAMPLE; + + /* fill in the check byte pattern */ + usb_buf[i++] = (stream << 1) | c; + } + } + } +} + +static inline void fill_out_urb(struct snd_usb_caiaqdev *dev, + struct urb *urb, + const struct usb_iso_packet_descriptor *iso) +{ + switch (dev->spec.data_alignment) { + case 0: + case 2: + fill_out_urb_mode_0(dev, urb, iso); + break; + case 3: + fill_out_urb_mode_3(dev, urb, iso); + break; } } diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index 91c804cd2782..00e5d0a469e1 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -55,6 +55,10 @@ static int control_info(struct snd_kcontrol *kcontrol, case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): maxval = 127; break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + maxval = 31; + break; } if (is_intval) { @@ -93,6 +97,7 @@ static int control_put(struct snd_kcontrol *kcontrol, struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); int pos = kcontrol->private_value; + int v = ucontrol->value.integer.value[0]; unsigned char cmd = EP1_CMD_WRITE_IO; if (dev->chip.usb_id == @@ -100,12 +105,27 @@ static int control_put(struct snd_kcontrol *kcontrol, cmd = EP1_CMD_DIMM_LEDS; if (pos & CNT_INTVAL) { - dev->control_state[pos & ~CNT_INTVAL] - = ucontrol->value.integer.value[0]; - snd_usb_caiaq_send_command(dev, cmd, - dev->control_state, sizeof(dev->control_state)); + int i = pos & ~CNT_INTVAL; + + dev->control_state[i] = v; + + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4)) { + int actual_len; + + dev->ep8_out_buf[0] = i; + dev->ep8_out_buf[1] = v; + + usb_bulk_msg(dev->chip.dev, + usb_sndbulkpipe(dev->chip.dev, 8), + dev->ep8_out_buf, sizeof(dev->ep8_out_buf), + &actual_len, 200); + } else { + snd_usb_caiaq_send_command(dev, cmd, + dev->control_state, sizeof(dev->control_state)); + } } else { - if (ucontrol->value.integer.value[0]) + if (v) dev->control_state[pos / 8] |= 1 << (pos % 8); else dev->control_state[pos / 8] &= ~(1 << (pos % 8)); @@ -296,6 +316,179 @@ static struct caiaq_controller kontrolx1_controller[] = { { "LED Deck B: SYNC", 8 | CNT_INTVAL }, }; +static struct caiaq_controller kontrols4_controller[] = { + { "LED: Master: Quant", 10 | CNT_INTVAL }, + { "LED: Master: Headphone", 11 | CNT_INTVAL }, + { "LED: Master: Master", 12 | CNT_INTVAL }, + { "LED: Master: Snap", 14 | CNT_INTVAL }, + { "LED: Master: Warning", 15 | CNT_INTVAL }, + { "LED: Master: Master button", 112 | CNT_INTVAL }, + { "LED: Master: Snap button", 113 | CNT_INTVAL }, + { "LED: Master: Rec", 118 | CNT_INTVAL }, + { "LED: Master: Size", 119 | CNT_INTVAL }, + { "LED: Master: Quant button", 120 | CNT_INTVAL }, + { "LED: Master: Browser button", 121 | CNT_INTVAL }, + { "LED: Master: Play button", 126 | CNT_INTVAL }, + { "LED: Master: Undo button", 127 | CNT_INTVAL }, + + { "LED: Channel A: >", 4 | CNT_INTVAL }, + { "LED: Channel A: <", 5 | CNT_INTVAL }, + { "LED: Channel A: Meter 1", 97 | CNT_INTVAL }, + { "LED: Channel A: Meter 2", 98 | CNT_INTVAL }, + { "LED: Channel A: Meter 3", 99 | CNT_INTVAL }, + { "LED: Channel A: Meter 4", 100 | CNT_INTVAL }, + { "LED: Channel A: Meter 5", 101 | CNT_INTVAL }, + { "LED: Channel A: Meter 6", 102 | CNT_INTVAL }, + { "LED: Channel A: Meter clip", 103 | CNT_INTVAL }, + { "LED: Channel A: Active", 114 | CNT_INTVAL }, + { "LED: Channel A: Cue", 116 | CNT_INTVAL }, + { "LED: Channel A: FX1", 149 | CNT_INTVAL }, + { "LED: Channel A: FX2", 148 | CNT_INTVAL }, + + { "LED: Channel B: >", 2 | CNT_INTVAL }, + { "LED: Channel B: <", 3 | CNT_INTVAL }, + { "LED: Channel B: Meter 1", 89 | CNT_INTVAL }, + { "LED: Channel B: Meter 2", 90 | CNT_INTVAL }, + { "LED: Channel B: Meter 3", 91 | CNT_INTVAL }, + { "LED: Channel B: Meter 4", 92 | CNT_INTVAL }, + { "LED: Channel B: Meter 5", 93 | CNT_INTVAL }, + { "LED: Channel B: Meter 6", 94 | CNT_INTVAL }, + { "LED: Channel B: Meter clip", 95 | CNT_INTVAL }, + { "LED: Channel B: Active", 122 | CNT_INTVAL }, + { "LED: Channel B: Cue", 125 | CNT_INTVAL }, + { "LED: Channel B: FX1", 147 | CNT_INTVAL }, + { "LED: Channel B: FX2", 146 | CNT_INTVAL }, + + { "LED: Channel C: >", 6 | CNT_INTVAL }, + { "LED: Channel C: <", 7 | CNT_INTVAL }, + { "LED: Channel C: Meter 1", 105 | CNT_INTVAL }, + { "LED: Channel C: Meter 2", 106 | CNT_INTVAL }, + { "LED: Channel C: Meter 3", 107 | CNT_INTVAL }, + { "LED: Channel C: Meter 4", 108 | CNT_INTVAL }, + { "LED: Channel C: Meter 5", 109 | CNT_INTVAL }, + { "LED: Channel C: Meter 6", 110 | CNT_INTVAL }, + { "LED: Channel C: Meter clip", 111 | CNT_INTVAL }, + { "LED: Channel C: Active", 115 | CNT_INTVAL }, + { "LED: Channel C: Cue", 117 | CNT_INTVAL }, + { "LED: Channel C: FX1", 151 | CNT_INTVAL }, + { "LED: Channel C: FX2", 150 | CNT_INTVAL }, + + { "LED: Channel D: >", 0 | CNT_INTVAL }, + { "LED: Channel D: <", 1 | CNT_INTVAL }, + { "LED: Channel D: Meter 1", 81 | CNT_INTVAL }, + { "LED: Channel D: Meter 2", 82 | CNT_INTVAL }, + { "LED: Channel D: Meter 3", 83 | CNT_INTVAL }, + { "LED: Channel D: Meter 4", 84 | CNT_INTVAL }, + { "LED: Channel D: Meter 5", 85 | CNT_INTVAL }, + { "LED: Channel D: Meter 6", 86 | CNT_INTVAL }, + { "LED: Channel D: Meter clip", 87 | CNT_INTVAL }, + { "LED: Channel D: Active", 123 | CNT_INTVAL }, + { "LED: Channel D: Cue", 124 | CNT_INTVAL }, + { "LED: Channel D: FX1", 145 | CNT_INTVAL }, + { "LED: Channel D: FX2", 144 | CNT_INTVAL }, + + { "LED: Deck A: 1 (blue)", 22 | CNT_INTVAL }, + { "LED: Deck A: 1 (green)", 23 | CNT_INTVAL }, + { "LED: Deck A: 2 (blue)", 20 | CNT_INTVAL }, + { "LED: Deck A: 2 (green)", 21 | CNT_INTVAL }, + { "LED: Deck A: 3 (blue)", 18 | CNT_INTVAL }, + { "LED: Deck A: 3 (green)", 19 | CNT_INTVAL }, + { "LED: Deck A: 4 (blue)", 16 | CNT_INTVAL }, + { "LED: Deck A: 4 (green)", 17 | CNT_INTVAL }, + { "LED: Deck A: Load", 44 | CNT_INTVAL }, + { "LED: Deck A: Deck C button", 45 | CNT_INTVAL }, + { "LED: Deck A: In", 47 | CNT_INTVAL }, + { "LED: Deck A: Out", 46 | CNT_INTVAL }, + { "LED: Deck A: Shift", 24 | CNT_INTVAL }, + { "LED: Deck A: Sync", 27 | CNT_INTVAL }, + { "LED: Deck A: Cue", 26 | CNT_INTVAL }, + { "LED: Deck A: Play", 25 | CNT_INTVAL }, + { "LED: Deck A: Tempo up", 33 | CNT_INTVAL }, + { "LED: Deck A: Tempo down", 32 | CNT_INTVAL }, + { "LED: Deck A: Master", 34 | CNT_INTVAL }, + { "LED: Deck A: Keylock", 35 | CNT_INTVAL }, + { "LED: Deck A: Deck A", 37 | CNT_INTVAL }, + { "LED: Deck A: Deck C", 36 | CNT_INTVAL }, + { "LED: Deck A: Samples", 38 | CNT_INTVAL }, + { "LED: Deck A: On Air", 39 | CNT_INTVAL }, + { "LED: Deck A: Sample 1", 31 | CNT_INTVAL }, + { "LED: Deck A: Sample 2", 30 | CNT_INTVAL }, + { "LED: Deck A: Sample 3", 29 | CNT_INTVAL }, + { "LED: Deck A: Sample 4", 28 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - A", 55 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - B", 54 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - C", 53 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - D", 52 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - E", 51 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - F", 50 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - G", 49 | CNT_INTVAL }, + { "LED: Deck A: Digit 1 - dot", 48 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - A", 63 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - B", 62 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - C", 61 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - D", 60 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - E", 59 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - F", 58 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - G", 57 | CNT_INTVAL }, + { "LED: Deck A: Digit 2 - dot", 56 | CNT_INTVAL }, + + { "LED: Deck B: 1 (blue)", 78 | CNT_INTVAL }, + { "LED: Deck B: 1 (green)", 79 | CNT_INTVAL }, + { "LED: Deck B: 2 (blue)", 76 | CNT_INTVAL }, + { "LED: Deck B: 2 (green)", 77 | CNT_INTVAL }, + { "LED: Deck B: 3 (blue)", 74 | CNT_INTVAL }, + { "LED: Deck B: 3 (green)", 75 | CNT_INTVAL }, + { "LED: Deck B: 4 (blue)", 72 | CNT_INTVAL }, + { "LED: Deck B: 4 (green)", 73 | CNT_INTVAL }, + { "LED: Deck B: Load", 180 | CNT_INTVAL }, + { "LED: Deck B: Deck D button", 181 | CNT_INTVAL }, + { "LED: Deck B: In", 183 | CNT_INTVAL }, + { "LED: Deck B: Out", 182 | CNT_INTVAL }, + { "LED: Deck B: Shift", 64 | CNT_INTVAL }, + { "LED: Deck B: Sync", 67 | CNT_INTVAL }, + { "LED: Deck B: Cue", 66 | CNT_INTVAL }, + { "LED: Deck B: Play", 65 | CNT_INTVAL }, + { "LED: Deck B: Tempo up", 185 | CNT_INTVAL }, + { "LED: Deck B: Tempo down", 184 | CNT_INTVAL }, + { "LED: Deck B: Master", 186 | CNT_INTVAL }, + { "LED: Deck B: Keylock", 187 | CNT_INTVAL }, + { "LED: Deck B: Deck B", 189 | CNT_INTVAL }, + { "LED: Deck B: Deck D", 188 | CNT_INTVAL }, + { "LED: Deck B: Samples", 190 | CNT_INTVAL }, + { "LED: Deck B: On Air", 191 | CNT_INTVAL }, + { "LED: Deck B: Sample 1", 71 | CNT_INTVAL }, + { "LED: Deck B: Sample 2", 70 | CNT_INTVAL }, + { "LED: Deck B: Sample 3", 69 | CNT_INTVAL }, + { "LED: Deck B: Sample 4", 68 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - A", 175 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - B", 174 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - C", 173 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - D", 172 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - E", 171 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - F", 170 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - G", 169 | CNT_INTVAL }, + { "LED: Deck B: Digit 1 - dot", 168 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - A", 167 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - B", 166 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - C", 165 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - D", 164 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - E", 163 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - F", 162 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - G", 161 | CNT_INTVAL }, + { "LED: Deck B: Digit 2 - dot", 160 | CNT_INTVAL }, + + { "LED: FX1: dry/wet", 153 | CNT_INTVAL }, + { "LED: FX1: 1", 154 | CNT_INTVAL }, + { "LED: FX1: 2", 155 | CNT_INTVAL }, + { "LED: FX1: 3", 156 | CNT_INTVAL }, + { "LED: FX1: Mode", 157 | CNT_INTVAL }, + { "LED: FX2: dry/wet", 129 | CNT_INTVAL }, + { "LED: FX2: 1", 130 | CNT_INTVAL }, + { "LED: FX2: 2", 131 | CNT_INTVAL }, + { "LED: FX2: 3", 132 | CNT_INTVAL }, + { "LED: FX2: Mode", 133 | CNT_INTVAL }, +}; + static int __devinit add_controls(struct caiaq_controller *c, int num, struct snd_usb_caiaqdev *dev) { @@ -354,6 +547,11 @@ int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) ret = add_controls(kontrolx1_controller, ARRAY_SIZE(kontrolx1_controller), dev); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + ret = add_controls(kontrols4_controller, + ARRAY_SIZE(kontrols4_controller), dev); + break; } return ret; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index cdfb856bddd2..6480c3283c05 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -36,7 +36,7 @@ #include "input.h" MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("caiaq USB audio, version 1.3.21"); +MODULE_DESCRIPTION("caiaq USB audio"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, RigKontrol3}," @@ -48,7 +48,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Audio 8 DJ}," "{Native Instruments, Session I/O}," "{Native Instruments, GuitarRig mobile}" - "{Native Instruments, Traktor Kontrol X1}"); + "{Native Instruments, Traktor Kontrol X1}" + "{Native Instruments, Traktor Kontrol S4}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ @@ -134,6 +135,11 @@ static struct usb_device_id snd_usb_id_table[] = { .idVendor = USB_VID_NATIVEINSTRUMENTS, .idProduct = USB_PID_TRAKTORKONTROLX1 }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_TRAKTORKONTROLS4 + }, { /* terminator */ } }; diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index f1117ecc84fd..e3d8a3efb35b 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -16,6 +16,7 @@ #define USB_PID_SESSIONIO 0x1915 #define USB_PID_GUITARRIGMOBILE 0x0d8d #define USB_PID_TRAKTORKONTROLX1 0x2305 +#define USB_PID_TRAKTORKONTROLS4 0xbaff #define EP1_BUFSIZE 64 #define EP4_BUFSIZE 512 @@ -99,13 +100,14 @@ struct snd_usb_caiaqdev { struct snd_pcm_substream *sub_capture[MAX_STREAMS]; /* Controls */ - unsigned char control_state[64]; + unsigned char control_state[256]; + unsigned char ep8_out_buf[2]; /* Linux input */ #ifdef CONFIG_SND_USB_CAIAQ_INPUT struct input_dev *input_dev; char phys[64]; /* physical device path */ - unsigned short keycode[64]; + unsigned short keycode[128]; struct urb *ep4_in_urb; unsigned char ep4_in_buf[EP4_BUFSIZE]; #endif diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index dcb620796d9e..4432ef7a70a9 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -67,7 +67,12 @@ static unsigned short keycode_kore[] = { KEY_BRL_DOT5 }; -#define KONTROLX1_INPUTS 40 +#define KONTROLX1_INPUTS (40) +#define KONTROLS4_BUTTONS (12 * 8) +#define KONTROLS4_AXIS (46) + +#define KONTROLS4_BUTTON(X) ((X) + BTN_MISC) +#define KONTROLS4_ABS(X) ((X) + ABS_HAT0X) #define DEG90 (range / 2) #define DEG180 (range) @@ -139,6 +144,13 @@ static unsigned int decode_erp(unsigned char a, unsigned char b) #undef HIGH_PEAK #undef LOW_PEAK +static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *dev, + int axis, const unsigned char *buf, + int offset) +{ + input_report_abs(dev->input_dev, axis, + (buf[offset * 2] << 8) | buf[offset * 2 + 1]); +} static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, const unsigned char *buf, @@ -148,36 +160,30 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): - input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]); - input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]); - input_sync(input_dev); + snd_caiaq_input_report_abs(dev, ABS_X, buf, 2); + snd_caiaq_input_report_abs(dev, ABS_Y, buf, 0); + snd_caiaq_input_report_abs(dev, ABS_Z, buf, 1); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); - input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); - input_sync(input_dev); - break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): - input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); - input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); - input_sync(input_dev); + snd_caiaq_input_report_abs(dev, ABS_X, buf, 0); + snd_caiaq_input_report_abs(dev, ABS_Y, buf, 1); + snd_caiaq_input_report_abs(dev, ABS_Z, buf, 2); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): - input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8) | buf[9]); - input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8) | buf[5]); - input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]); - input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8) | buf[3]); - input_report_abs(input_dev, ABS_HAT2X, (buf[14] << 8) | buf[15]); - input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]); - input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8) | buf[7]); - input_sync(input_dev); + snd_caiaq_input_report_abs(dev, ABS_HAT0X, buf, 4); + snd_caiaq_input_report_abs(dev, ABS_HAT0Y, buf, 2); + snd_caiaq_input_report_abs(dev, ABS_HAT1X, buf, 6); + snd_caiaq_input_report_abs(dev, ABS_HAT1Y, buf, 1); + snd_caiaq_input_report_abs(dev, ABS_HAT2X, buf, 7); + snd_caiaq_input_report_abs(dev, ABS_HAT2Y, buf, 0); + snd_caiaq_input_report_abs(dev, ABS_HAT3X, buf, 5); + snd_caiaq_input_report_abs(dev, ABS_HAT3Y, buf, 3); break; } + + input_sync(input_dev); } static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, @@ -250,6 +256,150 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, input_sync(input_dev); } +#define TKS4_MSGBLOCK_SIZE 16 + +static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev, + const unsigned char *buf, + unsigned int len) +{ + while (len) { + unsigned int i, block_id = (buf[0] << 8) | buf[1]; + + switch (block_id) { + case 0: + /* buttons */ + for (i = 0; i < KONTROLS4_BUTTONS; i++) + input_report_key(dev->input_dev, KONTROLS4_BUTTON(i), + (buf[4 + (i / 8)] >> (i % 8)) & 1); + break; + + case 1: + /* left wheel */ + input_report_abs(dev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8)); + /* right wheel */ + input_report_abs(dev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8)); + + /* rotary encoders */ + input_report_abs(dev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf); + input_report_abs(dev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4); + input_report_abs(dev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf); + input_report_abs(dev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4); + input_report_abs(dev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf); + input_report_abs(dev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4); + input_report_abs(dev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf); + input_report_abs(dev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4); + input_report_abs(dev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf); + + break; + case 2: + /* Volume Fader Channel D */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(0), buf, 1); + /* Volume Fader Channel B */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(1), buf, 2); + /* Volume Fader Channel A */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(2), buf, 3); + /* Volume Fader Channel C */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(3), buf, 4); + /* Loop Volume */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(4), buf, 6); + /* Crossfader */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(7), buf, 7); + + break; + + case 3: + /* Tempo Fader R */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(6), buf, 3); + /* Tempo Fader L */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(5), buf, 4); + /* Mic Volume */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(8), buf, 6); + /* Cue Mix */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(9), buf, 7); + + break; + + case 4: + /* Wheel distance sensor L */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(10), buf, 1); + /* Wheel distance sensor R */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(11), buf, 2); + /* Channel D EQ - Filter */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(12), buf, 3); + /* Channel D EQ - Low */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(13), buf, 4); + /* Channel D EQ - Mid */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(14), buf, 5); + /* Channel D EQ - Hi */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(15), buf, 6); + /* FX2 - dry/wet */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(16), buf, 7); + + break; + + case 5: + /* FX2 - 1 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(17), buf, 1); + /* FX2 - 2 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(18), buf, 2); + /* FX2 - 3 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(19), buf, 3); + /* Channel B EQ - Filter */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(20), buf, 4); + /* Channel B EQ - Low */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(21), buf, 5); + /* Channel B EQ - Mid */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(22), buf, 6); + /* Channel B EQ - Hi */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(23), buf, 7); + + break; + + case 6: + /* Channel A EQ - Filter */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(24), buf, 1); + /* Channel A EQ - Low */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(25), buf, 2); + /* Channel A EQ - Mid */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(26), buf, 3); + /* Channel A EQ - Hi */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(27), buf, 4); + /* Channel C EQ - Filter */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(28), buf, 5); + /* Channel C EQ - Low */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(29), buf, 6); + /* Channel C EQ - Mid */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(30), buf, 7); + + break; + + case 7: + /* Channel C EQ - Hi */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(31), buf, 1); + /* FX1 - wet/dry */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(32), buf, 2); + /* FX1 - 1 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(33), buf, 3); + /* FX1 - 2 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(34), buf, 4); + /* FX1 - 3 */ + snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(35), buf, 5); + + break; + + default: + debug("%s(): bogus block (id %d)\n", + __func__, block_id); + return; + } + + len -= TKS4_MSGBLOCK_SIZE; + buf += TKS4_MSGBLOCK_SIZE; + } + + input_sync(dev->input_dev); +} + static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) { struct snd_usb_caiaqdev *dev = urb->context; @@ -259,11 +409,11 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) if (urb->status || !dev || urb != dev->ep4_in_urb) return; - if (urb->actual_length < 24) - goto requeue; - switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (urb->actual_length < 24) + goto requeue; + if (buf[0] & 0x3) snd_caiaq_input_read_io(dev, buf + 1, 7); @@ -271,6 +421,10 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) snd_caiaq_input_read_analog(dev, buf + 8, 16); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length); + break; } requeue: @@ -289,6 +443,7 @@ static int snd_usb_caiaq_input_open(struct input_dev *idev) switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0) return -EIO; break; @@ -306,6 +461,7 @@ static void snd_usb_caiaq_input_close(struct input_dev *idev) switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): usb_kill_urb(dev->ep4_in_urb); break; } @@ -456,6 +612,46 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLS4_BUTTONS); + for (i = 0; i < KONTROLS4_BUTTONS; i++) + dev->keycode[i] = KONTROLS4_BUTTON(i); + input->keycodemax = KONTROLS4_BUTTONS; + + for (i = 0; i < KONTROLS4_AXIS; i++) { + int axis = KONTROLS4_ABS(i); + input->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); + } + + /* 36 analog potentiometers and faders */ + for (i = 0; i < 36; i++) + input_set_abs_params(input, KONTROLS4_ABS(i), 0, 0xfff, 0, 10); + + /* 2 encoder wheels */ + input_set_abs_params(input, KONTROLS4_ABS(36), 0, 0x3ff, 0, 1); + input_set_abs_params(input, KONTROLS4_ABS(37), 0, 0x3ff, 0, 1); + + /* 9 rotary encoders */ + for (i = 0; i < 9; i++) + input_set_abs_params(input, KONTROLS4_ABS(38+i), 0, 0xf, 0, 1); + + dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->ep4_in_urb) { + ret = -ENOMEM; + goto exit_free_idev; + } + + usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev, + usb_rcvbulkpipe(usb_dev, 0x4), + dev->ep4_in_buf, EP4_BUFSIZE, + snd_usb_caiaq_ep4_reply_dispatch, dev); + + snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5); + + break; + default: /* no input methods supported on this device */ goto exit_free_idev; diff --git a/sound/usb/card.c b/sound/usb/card.c index 9feb00c831a0..800f7cb4f251 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -126,7 +126,7 @@ static void snd_usb_stream_disconnect(struct list_head *head) for (idx = 0; idx < 2; idx++) { subs = &as->substream[idx]; if (!subs->num_formats) - return; + continue; snd_usb_release_substream_urbs(subs, 1); subs->interface = -1; } @@ -216,6 +216,11 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } switch (protocol) { + default: + snd_printdd(KERN_WARNING "unknown interface protocol %#02x, assuming v1\n", + protocol); + /* fall through */ + case UAC_VERSION_1: { struct uac1_ac_header_descriptor *h1 = control_header; @@ -253,10 +258,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) break; } - - default: - snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol); - return -EINVAL; } return 0; @@ -299,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; } @@ -377,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); @@ -465,7 +481,13 @@ static void *snd_usb_audio_probe(struct usb_device *dev, goto __error; } - chip->ctrl_intf = alts; + /* + * For devices with more than one control interface, we assume the + * first contains the audio controls. We might need a more specific + * check here in the future. + */ + if (!chip->ctrl_intf) + chip->ctrl_intf = alts; if (err > 0) { /* create normal USB audio interfaces */ diff --git a/sound/usb/card.h b/sound/usb/card.h index 1febf2f23754..ae4251d5abf7 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -62,12 +62,14 @@ struct snd_usb_substream { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ + int freqshift; /* how much to shift the feedback value to get Q16.16 */ unsigned int freqmax; /* maximum sampling rate, used for buffer management */ unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ + unsigned int syncmaxsize; /* sync endpoint packet size */ unsigned int fill_max: 1; /* fill max packet size always */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index b853f8df794f..7754a1034545 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -295,12 +295,11 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, switch (altsd->bInterfaceProtocol) { case UAC_VERSION_1: + default: return set_sample_rate_v1(chip, iface, alts, fmt, rate); case UAC_VERSION_2: return set_sample_rate_v2(chip, iface, alts, fmt, rate); } - - return -EINVAL; } diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 1a701f1e8f50..b0ef9f501896 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -275,6 +275,12 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) /* get audio formats */ switch (protocol) { + default: + snd_printdd(KERN_WARNING "%d:%u:%d: unknown interface protocol %#02x, assuming v1\n", + dev->devnum, iface_no, altno, protocol); + protocol = UAC_VERSION_1; + /* fall through */ + case UAC_VERSION_1: { struct uac1_as_header_descriptor *as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); @@ -336,11 +342,6 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) dev->devnum, iface_no, altno, as->bTerminalLink); continue; } - - default: - snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n", - dev->devnum, iface_no, altno, protocol); - continue; } /* get format type */ @@ -404,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/format.c b/sound/usb/format.c index 3a1375459c06..69148212aa70 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -49,7 +49,8 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, u64 pcm_formats; switch (protocol) { - case UAC_VERSION_1: { + case UAC_VERSION_1: + default: { struct uac_format_type_i_discrete_descriptor *fmt = _fmt; sample_width = fmt->bBitResolution; sample_bytes = fmt->bSubframeSize; @@ -64,9 +65,6 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, format <<= 1; break; } - - default: - return -EINVAL; } pcm_formats = 0; @@ -384,6 +382,10 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, * audio class v2 uses class specific EP0 range requests for that. */ switch (protocol) { + default: + snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n", + chip->dev->devnum, fp->iface, fp->altsetting, protocol); + /* fall through */ case UAC_VERSION_1: fp->channels = fmt->bNrChannels; ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7); @@ -392,10 +394,6 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, /* fp->channels is already set in this case */ ret = parse_audio_format_rates_v2(chip, fp); break; - default: - snd_printk(KERN_ERR "%d:%u:%d : invalid protocol version %d\n", - chip->dev->devnum, fp->iface, fp->altsetting, protocol); - return -EINVAL; } if (fp->channels < 1) { @@ -438,6 +436,10 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, fp->channels = 1; switch (protocol) { + default: + snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n", + chip->dev->devnum, fp->iface, fp->altsetting, protocol); + /* fall through */ case UAC_VERSION_1: { struct uac_format_type_ii_discrete_descriptor *fmt = _fmt; brate = le16_to_cpu(fmt->wMaxBitRate); @@ -456,10 +458,6 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, ret = parse_audio_format_rates_v2(chip, fp); break; } - default: - snd_printk(KERN_ERR "%d:%u:%d : invalid protocol version %d\n", - chip->dev->devnum, fp->iface, fp->altsetting, protocol); - return -EINVAL; } return ret; diff --git a/sound/usb/helper.c b/sound/usb/helper.c index d48d6f8f6ac9..f280c1903c25 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -103,11 +103,16 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, struct usb_host_interface *alts) { - if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH && - get_endpoint(alts, 0)->bInterval >= 1 && - get_endpoint(alts, 0)->bInterval <= 4) - return get_endpoint(alts, 0)->bInterval - 1; - else - return 0; + switch (snd_usb_get_speed(chip->dev)) { + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + if (get_endpoint(alts, 0)->bInterval >= 1 && + get_endpoint(alts, 0)->bInterval <= 4) + return get_endpoint(alts, 0)->bInterval - 1; + break; + default: + break; + } + return 0; } diff --git a/sound/usb/midi.c b/sound/usb/midi.c index b9c2bc65f51a..25bce7e5b1af 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 c166db0057d3..f2d74d654b3c 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; } @@ -2175,7 +2174,15 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, } host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; - mixer->protocol = get_iface_desc(host_iface)->bInterfaceProtocol; + switch (get_iface_desc(host_iface)->bInterfaceProtocol) { + case UAC_VERSION_1: + default: + mixer->protocol = UAC_VERSION_1; + break; + case UAC_VERSION_2: + mixer->protocol = UAC_VERSION_2; + break; + } if ((err = snd_usb_mixer_controls(mixer)) < 0 || (err = snd_usb_mixer_status_create(mixer)) < 0) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e7df1e5e3f2e..7dae05d8783e 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 3634cedf9306..cff3a3c465d7 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -173,13 +173,12 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, switch (altsd->bInterfaceProtocol) { case UAC_VERSION_1: + default: return init_pitch_v1(chip, iface, alts, fmt); case UAC_VERSION_2: return init_pitch_v2(chip, iface, alts, fmt); } - - return -EINVAL; } /* @@ -238,6 +237,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->datainterval = fmt->datainterval; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = fmt->maxpacksize; + subs->syncmaxsize = 0; subs->fill_max = 0; /* we need a sync pipe in async OUT or adaptive IN mode */ @@ -284,6 +284,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1; else subs->syncinterval = 3; + subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); } /* always fill max packet size */ @@ -467,7 +468,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); @@ -735,7 +736,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre } param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; - if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH) + if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) /* full speed devices have fixed data packet interval */ ptmin = 1000; if (ptmin == 1000) diff --git a/sound/usb/proc.c b/sound/usb/proc.c index f5e3f356b95f..961c9a250686 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); @@ -132,6 +132,11 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn ? get_full_speed_hz(subs->freqm) : get_high_speed_hz(subs->freqm), subs->freqm >> 16, subs->freqm & 0xffff); + if (subs->freqshift != INT_MIN) + snd_iprintf(buffer, " Feedback Format = %d.%d\n", + (subs->syncmaxsize > 3 ? 32 : 24) + - (16 - subs->freqshift), + 16 - subs->freqshift); } else { snd_iprintf(buffer, " Status: Stop\n"); } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 2e8003f98fca..ad7079d1676c 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 9a9da09586a5..cf8bf088394b 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 de607d4411ac..e184349aee83 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -225,6 +225,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, else subs->freqn = get_usb_high_speed_rate(rate); subs->freqm = subs->freqn; + subs->freqshift = INT_MIN; /* calculate max. frequency */ if (subs->maxpacksize) { /* whatever fits into a max. size packet */ @@ -244,7 +245,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; @@ -513,11 +514,10 @@ static int retire_paused_capture_urb(struct snd_usb_substream *subs, /* - * prepare urb for full speed playback sync pipe + * prepare urb for playback sync pipe * * set up the offset and length to receive the current frequency. */ - static int prepare_playback_sync_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) @@ -525,103 +525,78 @@ static int prepare_playback_sync_urb(struct snd_usb_substream *subs, struct snd_urb_ctx *ctx = urb->context; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize); urb->iso_frame_desc[0].offset = 0; return 0; } /* - * prepare urb for high speed playback sync pipe + * process after playback sync complete * - * set up the offset and length to receive the current frequency. - */ - -static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 4; - urb->iso_frame_desc[0].offset = 0; - return 0; -} - -/* - * process after full speed playback sync complete - * - * retrieve the current 10.14 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). + * Full speed devices report feedback values in 10.14 format as samples per + * frame, high speed devices in 16.16 format as samples per microframe. + * Because the Audio Class 1 spec was written before USB 2.0, many high speed + * devices use a wrong interpretation, some others use an entirely different + * format. Therefore, we cannot predict what format any particular device uses + * and must detect it automatically. */ static int retire_playback_sync_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) { unsigned int f; + int shift; unsigned long flags; - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 3) { - f = combine_triple((u8*)urb->transfer_buffer) << 2; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } - } - - return 0; -} + if (urb->iso_frame_desc[0].status != 0 || + urb->iso_frame_desc[0].actual_length < 3) + return 0; -/* - * process after high speed playback sync complete - * - * retrieve the current 12.13 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). - */ -static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; + f = le32_to_cpup(urb->transfer_buffer); + if (urb->iso_frame_desc[0].actual_length == 3) + f &= 0x00ffffff; + else + f &= 0x0fffffff; + if (f == 0) + return 0; - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); + if (unlikely(subs->freqshift == INT_MIN)) { + /* + * The first time we see a feedback value, determine its format + * by shifting it left or right until it matches the nominal + * frequency value. This assumes that the feedback does not + * differ from the nominal value more than +50% or -25%. + */ + shift = 0; + while (f < subs->freqn - subs->freqn / 4) { + f <<= 1; + shift++; + } + while (f > subs->freqn + subs->freqn / 2) { + f >>= 1; + shift--; } + subs->freqshift = shift; } + else if (subs->freqshift >= 0) + f <<= subs->freqshift; + else + f >>= -subs->freqshift; - return 0; -} - -/* - * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete - * - * These devices return the number of samples per packet instead of the number - * of samples per microframe. - */ -static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - f >>= subs->datainterval; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } + if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) { + /* + * If the frequency looks valid, set it. + * This value is referred to in prepare_playback_urb(). + */ + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } else { + /* + * Out of range; maybe the shift value is wrong. + * Reset it so that we autodetect again the next time. + */ + subs->freqshift = INT_MIN; } return 0; @@ -878,21 +853,6 @@ static struct snd_urb_ops audio_urb_ops[2] = { }, }; -static struct snd_urb_ops audio_urb_ops_high_speed[2] = { - { - .prepare = prepare_nodata_playback_urb, - .retire = retire_playback_urb, - .prepare_sync = prepare_playback_sync_urb_hs, - .retire_sync = retire_playback_sync_urb_hs, - }, - { - .prepare = prepare_capture_urb, - .retire = retire_capture_urb, - .prepare_sync = prepare_capture_sync_urb_hs, - .retire_sync = retire_capture_sync_urb, - }, -}; - /* * initialize the substream instance. */ @@ -909,23 +869,9 @@ void snd_usb_init_substream(struct snd_usb_stream *as, subs->direction = stream; subs->dev = as->chip->dev; subs->txfr_quirk = as->chip->txfr_quirk; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) { - subs->ops = audio_urb_ops[stream]; - } else { - subs->ops = audio_urb_ops_high_speed[stream]; - switch (as->chip->usb_id) { - case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ - case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ - case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ - subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; - break; - case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */ - case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ - subs->ops.prepare_sync = prepare_playback_sync_urb; - subs->ops.retire_sync = retire_playback_sync_urb; - break; - } - } + subs->ops = audio_urb_ops[stream]; + if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH) + subs->ops.prepare_sync = prepare_capture_sync_urb_hs; snd_usb_set_pcm_ops(as->pcm, stream); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 24d3319cc34d..db3eb21627ee 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 2a528e56afd5..287ef73b1237 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> |