diff options
author | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-11-03 14:11:27 +0000 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-11-03 14:11:27 +0000 |
commit | 8f987768eb99631374f4ab0bb19cd062baf1397d (patch) | |
tree | b89aa5c207f7ba6a688f45657424b937f17ceb8a /sound | |
parent | 63f7526f26f0a9291ac3f7a986aa18ebfb61ec19 (diff) | |
parent | c8ddb2713c624f432fa5fe3c7ecffcdda46ea0d4 (diff) | |
download | mrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.tar.gz mrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.tar.xz mrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.zip |
Merge commit 'v2.6.37-rc1' into for-2.6.37
Diffstat (limited to 'sound')
159 files changed, 14361 insertions, 47747 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index 070aab49019..45a818002d9 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 ec4a50ce565..57b792e2439 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -395,12 +395,10 @@ int snd_card_disconnect(struct snd_card *card) snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); snd_info_card_disconnect(card); -#ifndef CONFIG_SYSFS_DEPRECATED if (card->card_dev) { device_unregister(card->card_dev); card->card_dev = NULL; } -#endif #ifdef CONFIG_PM wake_up(&card->power_sleep); #endif @@ -573,7 +571,6 @@ void snd_card_set_id(struct snd_card *card, const char *nid) } EXPORT_SYMBOL(snd_card_set_id); -#ifndef CONFIG_SYSFS_DEPRECATED static ssize_t card_id_show_attr(struct device *dev, struct device_attribute *attr, char *buf) @@ -607,11 +604,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; @@ -630,7 +632,6 @@ card_number_show_attr(struct device *dev, static struct device_attribute card_number_attrs = __ATTR(number, S_IRUGO, card_number_show_attr, NULL); -#endif /* CONFIG_SYSFS_DEPRECATED */ /** * snd_card_register - register the soundcard @@ -649,7 +650,7 @@ int snd_card_register(struct snd_card *card) if (snd_BUG_ON(!card)) return -EINVAL; -#ifndef CONFIG_SYSFS_DEPRECATED + if (!card->card_dev) { card->card_dev = device_create(sound_class, card->dev, MKDEV(0, 0), card, @@ -657,7 +658,7 @@ int snd_card_register(struct snd_card *card) if (IS_ERR(card->card_dev)) card->card_dev = NULL; } -#endif + if ((err = snd_device_register_all(card)) < 0) return err; mutex_lock(&snd_card_mutex); @@ -674,7 +675,6 @@ int snd_card_register(struct snd_card *card) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); #endif -#ifndef CONFIG_SYSFS_DEPRECATED if (card->card_dev) { err = device_create_file(card->card_dev, &card_id_attrs); if (err < 0) @@ -683,7 +683,7 @@ int snd_card_register(struct snd_card *card) if (err < 0) return err; } -#endif + return 0; } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index f50ebf20df9..822dd56993c 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -77,7 +77,7 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file) struct snd_mixer_oss_file *fmixer; if (file->private_data) { - fmixer = (struct snd_mixer_oss_file *) file->private_data; + fmixer = file->private_data; module_put(fmixer->card->module); snd_card_file_remove(fmixer->card, file); kfree(fmixer); @@ -368,7 +368,7 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg); + return snd_mixer_oss_ioctl1(file->private_data, cmd, arg); } int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg) @@ -582,7 +582,7 @@ static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *left, int *right) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; *left = *right = 100; if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { @@ -618,8 +618,10 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) + if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { + up_read(&card->controls_rwsem); return; + } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) @@ -658,7 +660,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, return; down_read(&card->controls_rwsem); if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) { - up_read(&fmixer->card->controls_rwsem); + up_read(&card->controls_rwsem); return; } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); @@ -691,7 +693,7 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int left, int right) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); @@ -740,7 +742,7 @@ static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; int left, right; left = right = 1; @@ -753,7 +755,7 @@ static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int *active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; int left, right; left = right = 1; @@ -766,7 +768,7 @@ static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); return 0; @@ -776,7 +778,7 @@ static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer, struct snd_mixer_oss_slot *pslot, int active) { - struct slot *slot = (struct slot *)pslot->private_data; + struct slot *slot = pslot->private_data; snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); return 0; @@ -797,7 +799,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -813,7 +815,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (!(mixer->mask_recsrc & (1 << idx))) continue; pslot = &mixer->slots[idx]; - slot = (struct slot *)pslot->private_data; + slot = pslot->private_data; if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) continue; if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) @@ -826,6 +828,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -847,7 +850,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; - goto __unlock; + goto __free_only; } down_read(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); @@ -861,7 +864,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned if (!(mixer->mask_recsrc & (1 << idx))) continue; pslot = &mixer->slots[idx]; - slot = (struct slot *)pslot->private_data; + slot = pslot->private_data; if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) continue; if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) @@ -880,6 +883,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned err = 0; __unlock: up_read(&card->controls_rwsem); + __free_only: kfree(uctl); kfree(uinfo); return err; @@ -925,7 +929,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn) { - struct slot *p = (struct slot *)chn->private_data; + struct slot *p = chn->private_data; if (p) { if (p->allocated && p->assigned) { kfree(p->assigned->name); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index cbe815dfbdc..6b4b1287b31 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 e23e0e7ab26..a1707cca9c6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -334,11 +334,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, /* delta = "expected next hw_ptr" for in_interrupt != 0 */ delta = runtime->hw_ptr_interrupt + runtime->period_size; if (delta > new_hw_ptr) { - hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) - hw_base = 0; - new_hw_ptr = hw_base + pos; - goto __delta; + /* check for double acknowledged interrupts */ + hdelta = jiffies - runtime->hw_ptr_jiffies; + if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) + hw_base = 0; + new_hw_ptr = hw_base + pos; + goto __delta; + } } } /* new_hw_ptr might be lower than old_hw_ptr in case when */ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a3b2a647924..8bc7cb3db33 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -142,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, #ifdef RULES_DEBUG #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -char *snd_pcm_hw_param_names[] = { +static const char * const snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), @@ -864,6 +864,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); runtime->hw_ptr_jiffies = jiffies; + runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / + runtime->rate; runtime->status->state = state; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -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 eb68326c37d..cbbed0db9e5 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.c b/sound/core/seq/oss/seq_oss.c index f25e3cc7ddf..a1f1a2f00cc 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -220,6 +220,7 @@ static const struct file_operations seq_oss_f_ops = .poll = odev_poll, .unlocked_ioctl = odev_ioctl, .compat_ioctl = odev_ioctl_compat, + .llseek = noop_llseek, }; static int __init diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 685712276ac..69cd7b3c362 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/core/sound.c b/sound/core/sound.c index ac42af42b78..62a093efb45 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -184,7 +184,8 @@ static int snd_open(struct inode *inode, struct file *file) static const struct file_operations snd_fops = { .owner = THIS_MODULE, - .open = snd_open + .open = snd_open, + .llseek = noop_llseek, }; #ifdef CONFIG_SND_DYNAMIC_MINORS diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 480c38623da..c8961165277 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -74,6 +74,25 @@ config SND_DUMMY To compile this driver as a module, choose M here: the module will be called snd-dummy. +config SND_ALOOP + tristate "Generic loopback driver (PCM)" + select SND_PCM + help + Say 'Y' or 'M' to include support for the PCM loopback device. + This module returns played samples back to the user space using + the standard ALSA PCM device. The devices are routed 0->1 and + 1->0, where first number is the playback PCM device and second + number is the capture device. Module creates two PCM devices and + configured number of substreams (see the pcm_substreams module + parameter). + + The looback device allow time sychronization with an external + timing source using the time shift universal control (+-20% + of system time). + + To compile this driver as a module, choose M here: the module + will be called snd-aloop. + config SND_VIRMIDI tristate "Virtual MIDI soundcard" depends on SND_SEQUENCER diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index d4a07f9ff2c..1a8440c8b13 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -4,6 +4,7 @@ # snd-dummy-objs := dummy.o +snd-aloop-objs := aloop.o snd-mtpav-objs := mtpav.o snd-mts64-objs := mts64.o snd-portman2x4-objs := portman2x4.o @@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-dummy.o +obj-$(CONFIG_SND_ALOOP) += snd-aloop.o obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c new file mode 100644 index 00000000000..12b44b0b677 --- /dev/null +++ b/sound/drivers/aloop.c @@ -0,0 +1,1258 @@ +/* + * Loopback soundcard + * + * Original code: + * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * + * More accurate positioning and full-duplex support: + * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de> + * + * Major (almost complete) rewrite: + * Copyright (c) by Takashi Iwai <tiwai@suse.de> + * + * A next major update in 2010 (separate timers for playback and capture): + * Copyright (c) Jaroslav Kysela <perex@perex.cz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/info.h> +#include <sound/initval.h> + +MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); +MODULE_DESCRIPTION("A loopback soundcard"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); + +#define MAX_PCM_SUBSTREAMS 8 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; +static int pcm_notify[SNDRV_CARDS]; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for loopback soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for loopback soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable this loopback soundcard."); +module_param_array(pcm_substreams, int, NULL, 0444); +MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); +module_param_array(pcm_notify, int, NULL, 0444); +MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); + +#define NO_PITCH 100000 + +struct loopback_pcm; + +struct loopback_cable { + spinlock_t lock; + struct loopback_pcm *streams[2]; + struct snd_pcm_hardware hw; + /* flags */ + unsigned int valid; + unsigned int running; + unsigned int pause; +}; + +struct loopback_setup { + unsigned int notify: 1; + unsigned int rate_shift; + unsigned int format; + unsigned int rate; + unsigned int channels; + struct snd_ctl_elem_id active_id; + struct snd_ctl_elem_id format_id; + struct snd_ctl_elem_id rate_id; + struct snd_ctl_elem_id channels_id; +}; + +struct loopback { + struct snd_card *card; + struct mutex cable_lock; + struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; + struct snd_pcm *pcm[2]; + struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; +}; + +struct loopback_pcm { + struct loopback *loopback; + struct snd_pcm_substream *substream; + struct loopback_cable *cable; + unsigned int pcm_buffer_size; + unsigned int buf_pos; /* position in buffer */ + unsigned int silent_size; + /* PCM parameters */ + unsigned int pcm_period_size; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_salign; /* bytes per sample * channels */ + unsigned int pcm_rate_shift; /* rate shift value */ + /* flags */ + unsigned int period_update_pending :1; + /* timer stuff */ + unsigned int irq_pos; /* fractional IRQ position */ + unsigned int period_size_frac; + unsigned long last_jiffies; + struct timer_list timer; +}; + +static struct platform_device *devices[SNDRV_CARDS]; + +static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) +{ + if (dpcm->pcm_rate_shift == NO_PITCH) { + x /= HZ; + } else { + x = div_u64(NO_PITCH * (unsigned long long)x, + HZ * (unsigned long long)dpcm->pcm_rate_shift); + } + return x - (x % dpcm->pcm_salign); +} + +static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) +{ + if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */ + return x * HZ; + } else { + x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, + NO_PITCH); + } + return x; +} + +static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) +{ + int device = dpcm->substream->pstr->pcm->device; + + if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + device ^= 1; + return &dpcm->loopback->setup[dpcm->substream->number][device]; +} + +static inline unsigned int get_notify(struct loopback_pcm *dpcm) +{ + return get_setup(dpcm)->notify; +} + +static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) +{ + return get_setup(dpcm)->rate_shift; +} + +static void loopback_timer_start(struct loopback_pcm *dpcm) +{ + unsigned long tick; + unsigned int rate_shift = get_rate_shift(dpcm); + + if (rate_shift != dpcm->pcm_rate_shift) { + dpcm->pcm_rate_shift = rate_shift; + dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); + } + if (dpcm->period_size_frac <= dpcm->irq_pos) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + tick = dpcm->period_size_frac - dpcm->irq_pos; + tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; + dpcm->timer.expires = jiffies + tick; + add_timer(&dpcm->timer); +} + +static inline void loopback_timer_stop(struct loopback_pcm *dpcm) +{ + del_timer(&dpcm->timer); + dpcm->timer.expires = 0; +} + +#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) +#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE) +#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE) + +static int loopback_check_format(struct loopback_cable *cable, int stream) +{ + struct snd_pcm_runtime *runtime, *cruntime; + struct loopback_setup *setup; + struct snd_card *card; + int check; + + if (cable->valid != CABLE_VALID_BOTH) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + goto __notify; + return 0; + } + runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> + substream->runtime; + cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> + substream->runtime; + check = runtime->format != cruntime->format || + runtime->rate != cruntime->rate || + runtime->channels != cruntime->channels; + if (!check) + return 0; + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return -EIO; + } else { + snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> + substream, SNDRV_PCM_STATE_DRAINING); + __notify: + runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> + substream->runtime; + setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); + card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; + if (setup->format != runtime->format) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->format_id); + setup->format = runtime->format; + } + if (setup->rate != runtime->rate) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->rate_id); + setup->rate = runtime->rate; + } + if (setup->channels != runtime->channels) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->channels_id); + setup->channels = runtime->channels; + } + } + return 0; +} + +static void loopback_active_notify(struct loopback_pcm *dpcm) +{ + snd_ctl_notify(dpcm->loopback->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &get_setup(dpcm)->active_id); +} + +static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + int err, stream = 1 << substream->stream; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = loopback_check_format(cable, substream->stream); + if (err < 0) + return err; + dpcm->last_jiffies = jiffies; + dpcm->pcm_rate_shift = 0; + spin_lock(&cable->lock); + cable->running |= stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&cable->lock); + cable->running &= ~stream; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_stop(dpcm); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock(&cable->lock); + cable->pause |= stream; + spin_unlock(&cable->lock); + loopback_timer_stop(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock(&cable->lock); + dpcm->last_jiffies = jiffies; + cable->pause &= ~stream; + spin_unlock(&cable->lock); + loopback_timer_start(dpcm); + break; + default: + return -EINVAL; + } + return 0; +} + +static void params_change_substream(struct loopback_pcm *dpcm, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_runtime *dst_runtime; + + if (dpcm == NULL || dpcm->substream == NULL) + return; + dst_runtime = dpcm->substream->runtime; + if (dst_runtime == NULL) + return; + dst_runtime->hw = dpcm->cable->hw; +} + +static void params_change(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + + cable->hw.formats = (1ULL << runtime->format); + cable->hw.rate_min = runtime->rate; + cable->hw.rate_max = runtime->rate; + cable->hw.channels_min = runtime->channels; + cable->hw.channels_max = runtime->channels; + params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], + runtime); + params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], + runtime); +} + +static int loopback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + int bps, salign; + + salign = (snd_pcm_format_width(runtime->format) * + runtime->channels) / 8; + bps = salign * runtime->rate; + if (bps <= 0 || salign <= 0) + return -EINVAL; + + dpcm->buf_pos = 0; + dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /* clear capture buffer */ + dpcm->silent_size = dpcm->pcm_buffer_size; + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, + runtime->buffer_size * runtime->channels); + } + + dpcm->irq_pos = 0; + dpcm->period_update_pending = 0; + dpcm->pcm_bps = bps; + dpcm->pcm_salign = salign; + dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); + + mutex_lock(&dpcm->loopback->cable_lock); + if (!(cable->valid & ~(1 << substream->stream)) || + (get_setup(dpcm)->notify && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + params_change(substream); + cable->valid |= 1 << substream->stream; + mutex_unlock(&dpcm->loopback->cable_lock); + + return 0; +} + +static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) +{ + struct snd_pcm_runtime *runtime = dpcm->substream->runtime; + char *dst = runtime->dma_area; + unsigned int dst_off = dpcm->buf_pos; + + if (dpcm->silent_size >= dpcm->pcm_buffer_size) + return; + if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) + bytes = dpcm->pcm_buffer_size - dpcm->silent_size; + + for (;;) { + unsigned int size = bytes; + if (dst_off + size > dpcm->pcm_buffer_size) + size = dpcm->pcm_buffer_size - dst_off; + snd_pcm_format_set_silence(runtime->format, dst + dst_off, + bytes_to_frames(runtime, size) * + runtime->channels); + dpcm->silent_size += size; + bytes -= size; + if (!bytes) + break; + dst_off = 0; + } +} + +static void copy_play_buf(struct loopback_pcm *play, + struct loopback_pcm *capt, + unsigned int bytes) +{ + struct snd_pcm_runtime *runtime = play->substream->runtime; + char *src = runtime->dma_area; + char *dst = capt->substream->runtime->dma_area; + unsigned int src_off = play->buf_pos; + unsigned int dst_off = capt->buf_pos; + unsigned int clear_bytes = 0; + + /* check if playback is draining, trim the capture copy size + * when our pointer is at the end of playback ring buffer */ + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && + snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { + snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; + appl_ptr = appl_ptr1 = runtime->control->appl_ptr; + appl_ptr1 -= appl_ptr1 % runtime->buffer_size; + appl_ptr1 += play->buf_pos / play->pcm_salign; + if (appl_ptr < appl_ptr1) + appl_ptr1 -= runtime->buffer_size; + diff = (appl_ptr - appl_ptr1) * play->pcm_salign; + if (diff < bytes) { + clear_bytes = bytes - diff; + bytes = diff; + } + } + + for (;;) { + unsigned int size = bytes; + if (src_off + size > play->pcm_buffer_size) + size = play->pcm_buffer_size - src_off; + if (dst_off + size > capt->pcm_buffer_size) + size = capt->pcm_buffer_size - dst_off; + memcpy(dst + dst_off, src + src_off, size); + capt->silent_size = 0; + bytes -= size; + if (!bytes) + break; + src_off = (src_off + size) % play->pcm_buffer_size; + dst_off = (dst_off + size) % capt->pcm_buffer_size; + } + + if (clear_bytes > 0) { + clear_capture_buf(capt, clear_bytes); + capt->silent_size = 0; + } +} + +#define BYTEPOS_UPDATE_POSONLY 0 +#define BYTEPOS_UPDATE_CLEAR 1 +#define BYTEPOS_UPDATE_COPY 2 + +static void loopback_bytepos_update(struct loopback_pcm *dpcm, + unsigned int delta, + unsigned int cmd) +{ + unsigned int count; + unsigned long last_pos; + + last_pos = byte_pos(dpcm, dpcm->irq_pos); + dpcm->irq_pos += delta * dpcm->pcm_bps; + count = byte_pos(dpcm, dpcm->irq_pos) - last_pos; + if (!count) + return; + if (cmd == BYTEPOS_UPDATE_CLEAR) + clear_capture_buf(dpcm, count); + else if (cmd == BYTEPOS_UPDATE_COPY) + copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK], + dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE], + count); + dpcm->buf_pos += count; + dpcm->buf_pos %= dpcm->pcm_buffer_size; + if (dpcm->irq_pos >= dpcm->period_size_frac) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } +} + +static unsigned int loopback_pos_update(struct loopback_cable *cable) +{ + struct loopback_pcm *dpcm_play = + cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; + struct loopback_pcm *dpcm_capt = + cable->streams[SNDRV_PCM_STREAM_CAPTURE]; + unsigned long delta_play = 0, delta_capt = 0; + unsigned int running; + + spin_lock(&cable->lock); + running = cable->running ^ cable->pause; + if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { + delta_play = jiffies - dpcm_play->last_jiffies; + dpcm_play->last_jiffies += delta_play; + } + + if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { + delta_capt = jiffies - dpcm_capt->last_jiffies; + dpcm_capt->last_jiffies += delta_capt; + } + + if (delta_play == 0 && delta_capt == 0) { + spin_unlock(&cable->lock); + return running; + } + + if (delta_play > delta_capt) { + loopback_bytepos_update(dpcm_play, delta_play - delta_capt, + BYTEPOS_UPDATE_POSONLY); + delta_play = delta_capt; + } else if (delta_play < delta_capt) { + loopback_bytepos_update(dpcm_capt, delta_capt - delta_play, + BYTEPOS_UPDATE_CLEAR); + delta_capt = delta_play; + } + + if (delta_play == 0 && delta_capt == 0) { + spin_unlock(&cable->lock); + return running; + } + /* note delta_capt == delta_play at this moment */ + loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); + loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); + spin_unlock(&cable->lock); + return running; +} + +static void loopback_timer_function(unsigned long data) +{ + struct loopback_pcm *dpcm = (struct loopback_pcm *)data; + unsigned int running; + + running = loopback_pos_update(dpcm->cable); + if (running & (1 << dpcm->substream->stream)) { + loopback_timer_start(dpcm); + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + snd_pcm_period_elapsed(dpcm->substream); + } + } +} + +static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + loopback_pos_update(dpcm->cable); + return bytes_to_frames(runtime, dpcm->buf_pos); +} + +static struct snd_pcm_hardware loopback_pcm_hardware = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 32, + .buffer_bytes_max = 2 * 1024 * 1024, + .period_bytes_min = 64, + /* note check overflow in frac_pos() using pcm_rate_shift before + changing period_bytes_max value */ + .period_bytes_max = 1024 * 1024, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void loopback_runtime_free(struct snd_pcm_runtime *runtime) +{ + struct loopback_pcm *dpcm = runtime->private_data; + kfree(dpcm); +} + +static int loopback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); +} + +static int loopback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + + mutex_lock(&dpcm->loopback->cable_lock); + cable->valid &= ~(1 << substream->stream); + mutex_unlock(&dpcm->loopback->cable_lock); + return snd_pcm_lib_free_pages(substream); +} + +static unsigned int get_cable_index(struct snd_pcm_substream *substream) +{ + if (!substream->pcm->device) + return substream->stream; + else + return !substream->stream; +} + +static int rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + + struct snd_pcm_hardware *hw = rule->private; + struct snd_mask *maskp = hw_param_mask(params, rule->var); + + maskp->bits[0] &= (u_int32_t)hw->formats; + maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); + memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ + if (! maskp->bits[0] && ! maskp->bits[1]) + return -EINVAL; + return 0; +} + +static int rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hardware *hw = rule->private; + struct snd_interval t; + + t.min = hw->rate_min; + t.max = hw->rate_max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hardware *hw = rule->private; + struct snd_interval t; + + t.min = hw->channels_min; + t.max = hw->channels_max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int loopback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback *loopback = substream->private_data; + struct loopback_pcm *dpcm; + struct loopback_cable *cable; + int err = 0; + int dev = get_cable_index(substream); + + mutex_lock(&loopback->cable_lock); + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (!dpcm) { + err = -ENOMEM; + goto unlock; + } + dpcm->loopback = loopback; + dpcm->substream = substream; + setup_timer(&dpcm->timer, loopback_timer_function, + (unsigned long)dpcm); + + cable = loopback->cables[substream->number][dev]; + if (!cable) { + cable = kzalloc(sizeof(*cable), GFP_KERNEL); + if (!cable) { + kfree(dpcm); + err = -ENOMEM; + goto unlock; + } + spin_lock_init(&cable->lock); + cable->hw = loopback_pcm_hardware; + loopback->cables[substream->number][dev] = cable; + } + dpcm->cable = cable; + cable->streams[substream->stream] = dpcm; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* use dynamic rules based on actual runtime->hw values */ + /* note that the default rules created in the PCM midlevel code */ + /* are cached -> they do not reflect the actual state */ + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + rule_format, &runtime->hw, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + rule_rate, &runtime->hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + rule_channels, &runtime->hw, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto unlock; + + runtime->private_data = dpcm; + runtime->private_free = loopback_runtime_free; + if (get_notify(dpcm)) + runtime->hw = loopback_pcm_hardware; + else + runtime->hw = cable->hw; + unlock: + mutex_unlock(&loopback->cable_lock); + return err; +} + +static int loopback_close(struct snd_pcm_substream *substream) +{ + struct loopback *loopback = substream->private_data; + struct loopback_pcm *dpcm = substream->runtime->private_data; + struct loopback_cable *cable; + int dev = get_cable_index(substream); + + loopback_timer_stop(dpcm); + mutex_lock(&loopback->cable_lock); + cable = loopback->cables[substream->number][dev]; + if (cable->streams[!substream->stream]) { + /* other stream is still alive */ + cable->streams[substream->stream] = NULL; + } else { + /* free the cable */ + loopback->cables[substream->number][dev] = NULL; + kfree(cable); + } + mutex_unlock(&loopback->cable_lock); + return 0; +} + +static struct snd_pcm_ops loopback_playback_ops = { + .open = loopback_open, + .close = loopback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = loopback_hw_params, + .hw_free = loopback_hw_free, + .prepare = loopback_prepare, + .trigger = loopback_trigger, + .pointer = loopback_pointer, +}; + +static struct snd_pcm_ops loopback_capture_ops = { + .open = loopback_open, + .close = loopback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = loopback_hw_params, + .hw_free = loopback_hw_free, + .prepare = loopback_prepare, + .trigger = loopback_trigger, + .pointer = loopback_pointer, +}; + +static int __devinit loopback_pcm_new(struct loopback *loopback, + int device, int substreams) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(loopback->card, "Loopback PCM", device, + substreams, substreams, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops); + + pcm->private_data = loopback; + pcm->info_flags = 0; + strcpy(pcm->name, "Loopback PCM"); + + loopback->pcm[device] = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, 2 * 1024 * 1024); + return 0; +} + +static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 80000; + uinfo->value.integer.max = 120000; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].rate_shift; + return 0; +} + +static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.integer.value[0]; + if (val < 80000) + val = 80000; + if (val > 120000) + val = 120000; + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].rate_shift) { + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].rate_shift = val; + change = 1; + } + mutex_unlock(&loopback->cable_lock); + return change; +} + +static int loopback_notify_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].notify; + return 0; +} + +static int loopback_notify_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.integer.value[0] ? 1 : 0; + if (val != loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].notify) { + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].notify = val; + change = 1; + } + return change; +} + +static int loopback_active_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + struct loopback_cable *cable = loopback->cables + [kcontrol->id.subdevice][kcontrol->id.device ^ 1]; + unsigned int val = 0; + + if (cable != NULL) + val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? + 1 : 0; + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int loopback_format_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].format; + return 0; +} + +static int loopback_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].rate; + return 0; +} + +static int loopback_channels_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 1; + uinfo->value.integer.max = 1024; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_channels_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.subdevice] + [kcontrol->id.device].channels; + return 0; +} + +static struct snd_kcontrol_new loopback_controls[] __devinitdata = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Rate Shift 100000", + .info = loopback_rate_shift_info, + .get = loopback_rate_shift_get, + .put = loopback_rate_shift_put, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Notify", + .info = snd_ctl_boolean_mono_info, + .get = loopback_notify_get, + .put = loopback_notify_put, +}, +#define ACTIVE_IDX 2 +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Active", + .info = snd_ctl_boolean_mono_info, + .get = loopback_active_get, +}, +#define FORMAT_IDX 3 +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Format", + .info = loopback_format_info, + .get = loopback_format_get +}, +#define RATE_IDX 4 +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Rate", + .info = loopback_rate_info, + .get = loopback_rate_get +}, +#define CHANNELS_IDX 5 +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Channels", + .info = loopback_channels_info, + .get = loopback_channels_get +} +}; + +static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) +{ + struct snd_card *card = loopback->card; + struct snd_pcm *pcm; + struct snd_kcontrol *kctl; + struct loopback_setup *setup; + int err, dev, substr, substr_count, idx; + + strcpy(card->mixername, "Loopback Mixer"); + for (dev = 0; dev < 2; dev++) { + pcm = loopback->pcm[dev]; + substr_count = + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; + for (substr = 0; substr < substr_count; substr++) { + setup = &loopback->setup[substr][dev]; + setup->notify = notify; + setup->rate_shift = NO_PITCH; + setup->format = SNDRV_PCM_FORMAT_S16_LE; + setup->rate = 48000; + setup->channels = 2; + for (idx = 0; idx < ARRAY_SIZE(loopback_controls); + idx++) { + kctl = snd_ctl_new1(&loopback_controls[idx], + loopback); + if (!kctl) + return -ENOMEM; + kctl->id.device = dev; + kctl->id.subdevice = substr; + switch (idx) { + case ACTIVE_IDX: + setup->active_id = kctl->id; + break; + case FORMAT_IDX: + setup->format_id = kctl->id; + break; + case RATE_IDX: + setup->rate_id = kctl->id; + break; + case CHANNELS_IDX: + setup->channels_id = kctl->id; + break; + default: + break; + } + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + } + } + } + return 0; +} + +#ifdef CONFIG_PROC_FS + +static void print_dpcm_info(struct snd_info_buffer *buffer, + struct loopback_pcm *dpcm, + const char *id) +{ + snd_iprintf(buffer, " %s\n", id); + if (dpcm == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); + snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); + snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); + snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); + snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); + snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); + snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); + snd_iprintf(buffer, " update_pending:\t%u\n", + dpcm->period_update_pending); + snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); + snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); + snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", + dpcm->last_jiffies, jiffies); + snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); +} + +static void print_substream_info(struct snd_info_buffer *buffer, + struct loopback *loopback, + int sub, + int num) +{ + struct loopback_cable *cable = loopback->cables[sub][num]; + + snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); + if (cable == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " valid: %u\n", cable->valid); + snd_iprintf(buffer, " running: %u\n", cable->running); + snd_iprintf(buffer, " pause: %u\n", cable->pause); + print_dpcm_info(buffer, cable->streams[0], "Playback"); + print_dpcm_info(buffer, cable->streams[1], "Capture"); +} + +static void print_cable_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct loopback *loopback = entry->private_data; + int sub, num; + + mutex_lock(&loopback->cable_lock); + num = entry->name[strlen(entry->name)-1]; + num = num == '0' ? 0 : 1; + for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) + print_substream_info(buffer, loopback, sub, num); + mutex_unlock(&loopback->cable_lock); +} + +static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) +{ + char name[32]; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "cable#%d", cidx); + err = snd_card_proc_new(loopback->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, loopback, print_cable_info); + return 0; +} + +#else /* !CONFIG_PROC_FS */ + +#define loopback_proc_new(loopback, cidx) do { } while (0) + +#endif + +static int __devinit loopback_probe(struct platform_device *devptr) +{ + struct snd_card *card; + struct loopback *loopback; + int dev = devptr->id; + int err; + + err = snd_card_create(index[dev], id[dev], THIS_MODULE, + sizeof(struct loopback), &card); + if (err < 0) + return err; + loopback = card->private_data; + + if (pcm_substreams[dev] < 1) + pcm_substreams[dev] = 1; + if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) + pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; + + loopback->card = card; + mutex_init(&loopback->cable_lock); + + err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]); + if (err < 0) + goto __nodev; + err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]); + if (err < 0) + goto __nodev; + err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); + if (err < 0) + goto __nodev; + loopback_proc_new(loopback, 0); + loopback_proc_new(loopback, 1); + strcpy(card->driver, "Loopback"); + strcpy(card->shortname, "Loopback"); + sprintf(card->longname, "Loopback %i", dev + 1); + err = snd_card_register(card); + if (!err) { + platform_set_drvdata(devptr, card); + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __devexit loopback_remove(struct platform_device *devptr) +{ + snd_card_free(platform_get_drvdata(devptr)); + platform_set_drvdata(devptr, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int loopback_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_card *card = platform_get_drvdata(pdev); + struct loopback *loopback = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + + snd_pcm_suspend_all(loopback->pcm[0]); + snd_pcm_suspend_all(loopback->pcm[1]); + return 0; +} + +static int loopback_resume(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + +#define SND_LOOPBACK_DRIVER "snd_aloop" + +static struct platform_driver loopback_driver = { + .probe = loopback_probe, + .remove = __devexit_p(loopback_remove), +#ifdef CONFIG_PM + .suspend = loopback_suspend, + .resume = loopback_resume, +#endif + .driver = { + .name = SND_LOOPBACK_DRIVER + }, +}; + +static void loopback_unregister_all(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(devices); ++i) + platform_device_unregister(devices[i]); + platform_driver_unregister(&loopback_driver); +} + +static int __init alsa_card_loopback_init(void) +{ + int i, err, cards; + + err = platform_driver_register(&loopback_driver); + if (err < 0) + return err; + + + cards = 0; + for (i = 0; i < SNDRV_CARDS; i++) { + struct platform_device *device; + if (!enable[i]) + continue; + device = platform_device_register_simple(SND_LOOPBACK_DRIVER, + i, NULL, 0); + if (IS_ERR(device)) + continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } + devices[i] = device; + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "aloop: No loopback enabled\n"); +#endif + loopback_unregister_all(); + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_loopback_exit(void) +{ + loopback_unregister_all(); +} + +module_init(alsa_card_loopback_init) +module_exit(alsa_card_loopback_exit) diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 0e631c3221e..f4cd49336f3 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -94,7 +94,7 @@ static int __devinit snd_virmidi_probe(struct platform_device *devptr) sizeof(struct snd_card_virmidi), &card); if (err < 0) return err; - vmidi = (struct snd_card_virmidi *)card->private_data; + vmidi = card->private_data; vmidi->card = card; if (midi_devs[dev] > MAX_MIDI_DEVICES) { diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 1adb8a3c2b6..57ccba88700 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -878,7 +878,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) static void proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data; + struct snd_akm4xxx *ak = entry->private_data; int reg, val, chip; for (chip = 0; chip < ak->num_chips; chip++) { for (reg = 0; reg < ak->total_regs; reg++) { @@ -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 c6990c68079..52064cfa91f 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -77,6 +77,32 @@ config SND_ALS100 To compile this driver as a module, choose M here: the module will be called snd-als100. +config SND_AZT1605 + tristate "Aztech AZT1605 Driver" + depends on SND + select SND_WSS_LIB + select SND_MPU401_UART + select SND_OPL3_LIB + help + Say Y here to include support for Aztech Sound Galaxy cards + based on the AZT1605 chipset. + + To compile this driver as a module, choose M here: the module + will be called snd-azt1605. + +config SND_AZT2316 + tristate "Aztech AZT2316 Driver" + depends on SND + select SND_WSS_LIB + select SND_MPU401_UART + select SND_OPL3_LIB + help + Say Y here to include support for Aztech Sound Galaxy cards + based on the AZT2316 chipset. + + To compile this driver as a module, choose M here: the module + will be called snd-azt2316. + config SND_AZT2320 tristate "Aztech Systems AZT2320" depends on PNP @@ -351,16 +377,6 @@ config SND_SB16_CSP coprocessor can do variable tasks like various compression and decompression algorithms. -config SND_SGALAXY - tristate "Aztech Sound Galaxy" - select SND_WSS_LIB - help - Say Y here to include support for Aztech Sound Galaxy - soundcards. - - To compile this driver as a module, choose M here: the module - will be called snd-sgalaxy. - config SND_SSCAPE tristate "Ensoniq SoundScape driver" select SND_MPU401_UART diff --git a/sound/isa/Makefile b/sound/isa/Makefile index c73d30c4f46..8d781e419e2 100644 --- a/sound/isa/Makefile +++ b/sound/isa/Makefile @@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o snd-es18xx-objs := es18xx.o snd-opl3sa2-objs := opl3sa2.o snd-sc6000-objs := sc6000.o -snd-sgalaxy-objs := sgalaxy.o snd-sscape-objs := sscape.o # Toplevel Module Dependency @@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o obj-$(CONFIG_SND_SC6000) += snd-sc6000.o -obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o -obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \ +obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \ sb/ wavefront/ wss/ diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index bbcbf92a8eb..3cb75bc9769 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -162,7 +162,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard sizeof(struct snd_card_ad1816a), &card); if (error < 0) return error; - acard = (struct snd_card_ad1816a *)card->private_data; + acard = card->private_data; if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) { snd_card_free(card); diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index f7aa637b0d1..aac8dc15c2f 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -188,7 +188,7 @@ static int __devinit snd_card_azt2320_probe(int dev, sizeof(struct snd_card_azt2320), &card); if (error < 0) return error; - acard = (struct snd_card_azt2320 *)card->private_data; + acard = card->private_data; if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) { snd_card_free(card); diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile new file mode 100644 index 00000000000..e307066d431 --- /dev/null +++ b/sound/isa/galaxy/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# + +snd-azt1605-objs := azt1605.o +snd-azt2316-objs := azt2316.o + +obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o +obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c new file mode 100644 index 00000000000..9a97643cb71 --- /dev/null +++ b/sound/isa/galaxy/azt1605.c @@ -0,0 +1,91 @@ +/* + * Aztech AZT1605 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#define AZT1605 + +#define CRD_NAME "Aztech AZT1605" +#define DRV_NAME "AZT1605" +#define DEV_NAME "azt1605" + +#define GALAXY_DSP_MAJOR 2 +#define GALAXY_DSP_MINOR 1 + +#define GALAXY_CONFIG_SIZE 3 + +/* + * 24-bit config register + */ + +#define GALAXY_CONFIG_SBA_220 (0 << 0) +#define GALAXY_CONFIG_SBA_240 (1 << 0) +#define GALAXY_CONFIG_SBA_260 (2 << 0) +#define GALAXY_CONFIG_SBA_280 (3 << 0) +#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 + +#define GALAXY_CONFIG_MPUA_300 (0 << 2) +#define GALAXY_CONFIG_MPUA_330 (1 << 2) + +#define GALAXY_CONFIG_MPU_ENABLE (1 << 3) + +#define GALAXY_CONFIG_GAME_ENABLE (1 << 4) + +#define GALAXY_CONFIG_CD_PANASONIC (1 << 5) +#define GALAXY_CONFIG_CD_MITSUMI (1 << 6) +#define GALAXY_CONFIG_CD_MASK (\ + GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI) + +#define GALAXY_CONFIG_UNUSED (1 << 7) +#define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED + +#define GALAXY_CONFIG_SBIRQ_2 (1 << 8) +#define GALAXY_CONFIG_SBIRQ_3 (1 << 9) +#define GALAXY_CONFIG_SBIRQ_5 (1 << 10) +#define GALAXY_CONFIG_SBIRQ_7 (1 << 11) + +#define GALAXY_CONFIG_MPUIRQ_2 (1 << 12) +#define GALAXY_CONFIG_MPUIRQ_3 (1 << 13) +#define GALAXY_CONFIG_MPUIRQ_5 (1 << 14) +#define GALAXY_CONFIG_MPUIRQ_7 (1 << 15) + +#define GALAXY_CONFIG_WSSA_530 (0 << 16) +#define GALAXY_CONFIG_WSSA_604 (1 << 16) +#define GALAXY_CONFIG_WSSA_E80 (2 << 16) +#define GALAXY_CONFIG_WSSA_F40 (3 << 16) + +#define GALAXY_CONFIG_WSS_ENABLE (1 << 18) + +#define GALAXY_CONFIG_CDIRQ_11 (1 << 19) +#define GALAXY_CONFIG_CDIRQ_12 (1 << 20) +#define GALAXY_CONFIG_CDIRQ_15 (1 << 21) +#define GALAXY_CONFIG_CDIRQ_MASK (\ + GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\ + GALAXY_CONFIG_CDIRQ_15) + +#define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22) +#define GALAXY_CONFIG_CDDMA_0 (1 << 22) +#define GALAXY_CONFIG_CDDMA_1 (2 << 22) +#define GALAXY_CONFIG_CDDMA_3 (3 << 22) +#define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3 + +#define GALAXY_CONFIG_MASK (\ + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\ + GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\ + GALAXY_CONFIG_CDDMA_MASK) + +#include "galaxy.c" diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c new file mode 100644 index 00000000000..189441141df --- /dev/null +++ b/sound/isa/galaxy/azt2316.c @@ -0,0 +1,111 @@ +/* + * Aztech AZT2316 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#define AZT2316 + +#define CRD_NAME "Aztech AZT2316" +#define DRV_NAME "AZT2316" +#define DEV_NAME "azt2316" + +#define GALAXY_DSP_MAJOR 3 +#define GALAXY_DSP_MINOR 1 + +#define GALAXY_CONFIG_SIZE 4 + +/* + * 32-bit config register + */ + +#define GALAXY_CONFIG_SBA_220 (0 << 0) +#define GALAXY_CONFIG_SBA_240 (1 << 0) +#define GALAXY_CONFIG_SBA_260 (2 << 0) +#define GALAXY_CONFIG_SBA_280 (3 << 0) +#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 + +#define GALAXY_CONFIG_SBIRQ_2 (1 << 2) +#define GALAXY_CONFIG_SBIRQ_5 (1 << 3) +#define GALAXY_CONFIG_SBIRQ_7 (1 << 4) +#define GALAXY_CONFIG_SBIRQ_10 (1 << 5) + +#define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6) +#define GALAXY_CONFIG_SBDMA_0 (1 << 6) +#define GALAXY_CONFIG_SBDMA_1 (2 << 6) +#define GALAXY_CONFIG_SBDMA_3 (3 << 6) + +#define GALAXY_CONFIG_WSSA_530 (0 << 8) +#define GALAXY_CONFIG_WSSA_604 (1 << 8) +#define GALAXY_CONFIG_WSSA_E80 (2 << 8) +#define GALAXY_CONFIG_WSSA_F40 (3 << 8) + +#define GALAXY_CONFIG_WSS_ENABLE (1 << 10) + +#define GALAXY_CONFIG_GAME_ENABLE (1 << 11) + +#define GALAXY_CONFIG_MPUA_300 (0 << 12) +#define GALAXY_CONFIG_MPUA_330 (1 << 12) + +#define GALAXY_CONFIG_MPU_ENABLE (1 << 13) + +#define GALAXY_CONFIG_CDA_310 (0 << 14) +#define GALAXY_CONFIG_CDA_320 (1 << 14) +#define GALAXY_CONFIG_CDA_340 (2 << 14) +#define GALAXY_CONFIG_CDA_350 (3 << 14) +#define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350 + +#define GALAXY_CONFIG_CD_DISABLE (0 << 16) +#define GALAXY_CONFIG_CD_PANASONIC (1 << 16) +#define GALAXY_CONFIG_CD_SONY (2 << 16) +#define GALAXY_CONFIG_CD_MITSUMI (3 << 16) +#define GALAXY_CONFIG_CD_AZTECH (4 << 16) +#define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16) +#define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16) +#define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16) +#define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7 + +#define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20) +#define GALAXY_CONFIG_CDDMA8_0 (1 << 20) +#define GALAXY_CONFIG_CDDMA8_1 (2 << 20) +#define GALAXY_CONFIG_CDDMA8_3 (3 << 20) +#define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3 + +#define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22) +#define GALAXY_CONFIG_CDDMA16_5 (1 << 22) +#define GALAXY_CONFIG_CDDMA16_6 (2 << 22) +#define GALAXY_CONFIG_CDDMA16_7 (3 << 22) +#define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7 + +#define GALAXY_CONFIG_MPUIRQ_2 (1 << 24) +#define GALAXY_CONFIG_MPUIRQ_5 (1 << 25) +#define GALAXY_CONFIG_MPUIRQ_7 (1 << 26) +#define GALAXY_CONFIG_MPUIRQ_10 (1 << 27) + +#define GALAXY_CONFIG_CDIRQ_5 (1 << 28) +#define GALAXY_CONFIG_CDIRQ_11 (1 << 29) +#define GALAXY_CONFIG_CDIRQ_12 (1 << 30) +#define GALAXY_CONFIG_CDIRQ_15 (1 << 31) +#define GALAXY_CONFIG_CDIRQ_MASK (\ + GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\ + GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15) + +#define GALAXY_CONFIG_MASK (\ + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\ + GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\ + GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK) + +#include "galaxy.c" diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c new file mode 100644 index 00000000000..ee54df082b9 --- /dev/null +++ b/sound/isa/galaxy/galaxy.c @@ -0,0 +1,652 @@ +/* + * Aztech AZT1605/AZT2316 Driver + * Copyright (C) 2007,2010 Rene Herman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/isa.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <asm/processor.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/wss.h> +#include <sound/mpu401.h> +#include <sound/opl3.h> + +MODULE_DESCRIPTION(CRD_NAME); +MODULE_AUTHOR("Rene Herman"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); + +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +module_param_array(port, long, NULL, 0444); +MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); +module_param_array(wss_port, long, NULL, 0444); +MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver."); +module_param_array(mpu_port, long, NULL, 0444); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); +module_param_array(fm_port, long, NULL, 0444); +MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver."); +module_param_array(irq, int, NULL, 0444); +MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); +module_param_array(mpu_irq, int, NULL, 0444); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); +module_param_array(dma1, int, NULL, 0444); +MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver."); +module_param_array(dma2, int, NULL, 0444); +MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver."); + +/* + * Generic SB DSP support routines + */ + +#define DSP_PORT_RESET 0x6 +#define DSP_PORT_READ 0xa +#define DSP_PORT_COMMAND 0xc +#define DSP_PORT_STATUS 0xc +#define DSP_PORT_DATA_AVAIL 0xe + +#define DSP_SIGNATURE 0xaa + +#define DSP_COMMAND_GET_VERSION 0xe1 + +static int __devinit dsp_get_byte(void __iomem *port, u8 *val) +{ + int loops = 1000; + + while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) { + if (!loops--) + return -EIO; + cpu_relax(); + } + *val = ioread8(port + DSP_PORT_READ); + return 0; +} + +static int __devinit dsp_reset(void __iomem *port) +{ + u8 val; + + iowrite8(1, port + DSP_PORT_RESET); + udelay(10); + iowrite8(0, port + DSP_PORT_RESET); + + if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE) + return -ENODEV; + + return 0; +} + +static int __devinit dsp_command(void __iomem *port, u8 cmd) +{ + int loops = 1000; + + while (ioread8(port + DSP_PORT_STATUS) & 0x80) { + if (!loops--) + return -EIO; + cpu_relax(); + } + iowrite8(cmd, port + DSP_PORT_COMMAND); + return 0; +} + +static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor) +{ + int err; + + err = dsp_command(port, DSP_COMMAND_GET_VERSION); + if (err < 0) + return err; + + err = dsp_get_byte(port, major); + if (err < 0) + return err; + + err = dsp_get_byte(port, minor); + if (err < 0) + return err; + + return 0; +} + +/* + * Generic WSS support routines + */ + +#define WSS_CONFIG_DMA_0 (1 << 0) +#define WSS_CONFIG_DMA_1 (2 << 0) +#define WSS_CONFIG_DMA_3 (3 << 0) +#define WSS_CONFIG_DUPLEX (1 << 2) +#define WSS_CONFIG_IRQ_7 (1 << 3) +#define WSS_CONFIG_IRQ_9 (2 << 3) +#define WSS_CONFIG_IRQ_10 (3 << 3) +#define WSS_CONFIG_IRQ_11 (4 << 3) + +#define WSS_PORT_CONFIG 0 +#define WSS_PORT_SIGNATURE 3 + +#define WSS_SIGNATURE 4 + +static int __devinit wss_detect(void __iomem *wss_port) +{ + if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE) + return -ENODEV; + + return 0; +} + +static void wss_set_config(void __iomem *wss_port, u8 wss_config) +{ + iowrite8(wss_config, wss_port + WSS_PORT_CONFIG); +} + +/* + * Aztech Sound Galaxy specifics + */ + +#define GALAXY_PORT_CONFIG 1024 +#define CONFIG_PORT_SET 4 + +#define DSP_COMMAND_GALAXY_8 8 +#define GALAXY_COMMAND_GET_TYPE 5 + +#define DSP_COMMAND_GALAXY_9 9 +#define GALAXY_COMMAND_WSSMODE 0 +#define GALAXY_COMMAND_SB8MODE 1 + +#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE +#define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE + +struct snd_galaxy { + void __iomem *port; + void __iomem *config_port; + void __iomem *wss_port; + u32 config; + struct resource *res_port; + struct resource *res_config_port; + struct resource *res_wss_port; +}; + +static u32 config[SNDRV_CARDS]; +static u8 wss_config[SNDRV_CARDS]; + +static int __devinit snd_galaxy_match(struct device *dev, unsigned int n) +{ + if (!enable[n]) + return 0; + + switch (port[n]) { + case SNDRV_AUTO_PORT: + dev_err(dev, "please specify port\n"); + return 0; + case 0x220: + config[n] |= GALAXY_CONFIG_SBA_220; + break; + case 0x240: + config[n] |= GALAXY_CONFIG_SBA_240; + break; + case 0x260: + config[n] |= GALAXY_CONFIG_SBA_260; + break; + case 0x280: + config[n] |= GALAXY_CONFIG_SBA_280; + break; + default: + dev_err(dev, "invalid port %#lx\n", port[n]); + return 0; + } + + switch (wss_port[n]) { + case SNDRV_AUTO_PORT: + dev_err(dev, "please specify wss_port\n"); + return 0; + case 0x530: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530; + break; + case 0x604: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604; + break; + case 0xe80: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80; + break; + case 0xf40: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40; + break; + default: + dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]); + return 0; + } + + switch (irq[n]) { + case SNDRV_AUTO_IRQ: + dev_err(dev, "please specify irq\n"); + return 0; + case 7: + wss_config[n] |= WSS_CONFIG_IRQ_7; + break; + case 2: + irq[n] = 9; + case 9: + wss_config[n] |= WSS_CONFIG_IRQ_9; + break; + case 10: + wss_config[n] |= WSS_CONFIG_IRQ_10; + break; + case 11: + wss_config[n] |= WSS_CONFIG_IRQ_11; + break; + default: + dev_err(dev, "invalid IRQ %d\n", irq[n]); + return 0; + } + + switch (dma1[n]) { + case SNDRV_AUTO_DMA: + dev_err(dev, "please specify dma1\n"); + return 0; + case 0: + wss_config[n] |= WSS_CONFIG_DMA_0; + break; + case 1: + wss_config[n] |= WSS_CONFIG_DMA_1; + break; + case 3: + wss_config[n] |= WSS_CONFIG_DMA_3; + break; + default: + dev_err(dev, "invalid playback DMA %d\n", dma1[n]); + return 0; + } + + if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) { + dma2[n] = -1; + goto mpu; + } + + wss_config[n] |= WSS_CONFIG_DUPLEX; + switch (dma2[n]) { + case 0: + break; + case 1: + if (dma1[n] == 0) + break; + default: + dev_err(dev, "invalid capture DMA %d\n", dma2[n]); + return 0; + } + +mpu: + switch (mpu_port[n]) { + case SNDRV_AUTO_PORT: + dev_warn(dev, "mpu_port not specified; not using MPU-401\n"); + mpu_port[n] = -1; + goto fm; + case 0x300: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300; + break; + case 0x330: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330; + break; + default: + dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]); + return 0; + } + + switch (mpu_irq[n]) { + case SNDRV_AUTO_IRQ: + dev_warn(dev, "mpu_irq not specified: using polling mode\n"); + mpu_irq[n] = -1; + break; + case 2: + mpu_irq[n] = 9; + case 9: + config[n] |= GALAXY_CONFIG_MPUIRQ_2; + break; +#ifdef AZT1605 + case 3: + config[n] |= GALAXY_CONFIG_MPUIRQ_3; + break; +#endif + case 5: + config[n] |= GALAXY_CONFIG_MPUIRQ_5; + break; + case 7: + config[n] |= GALAXY_CONFIG_MPUIRQ_7; + break; +#ifdef AZT2316 + case 10: + config[n] |= GALAXY_CONFIG_MPUIRQ_10; + break; +#endif + default: + dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]); + return 0; + } + + if (mpu_irq[n] == irq[n]) { + dev_err(dev, "cannot share IRQ between WSS and MPU-401\n"); + return 0; + } + +fm: + switch (fm_port[n]) { + case SNDRV_AUTO_PORT: + dev_warn(dev, "fm_port not specified: not using OPL3\n"); + fm_port[n] = -1; + break; + case 0x388: + break; + default: + dev_err(dev, "illegal FM port %#lx\n", fm_port[n]); + return 0; + } + + config[n] |= GALAXY_CONFIG_GAME_ENABLE; + return 1; +} + +static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type) +{ + u8 major; + u8 minor; + int err; + + err = dsp_reset(galaxy->port); + if (err < 0) + return err; + + err = dsp_get_version(galaxy->port, &major, &minor); + if (err < 0) + return err; + + if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR) + return -ENODEV; + + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8); + if (err < 0) + return err; + + err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE); + if (err < 0) + return err; + + err = dsp_get_byte(galaxy->port, type); + if (err < 0) + return err; + + return 0; +} + +static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode) +{ + int err; + + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9); + if (err < 0) + return err; + + err = dsp_command(galaxy->port, mode); + if (err < 0) + return err; + +#ifdef AZT1605 + /* + * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again + */ + err = dsp_reset(galaxy->port); + if (err < 0) + return err; +#endif + + return 0; +} + +static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config) +{ + u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET); + int i; + + iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET); + for (i = 0; i < GALAXY_CONFIG_SIZE; i++) { + iowrite8(config, galaxy->config_port + i); + config >>= 8; + } + iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET); + msleep(10); +} + +static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config) +{ + int i; + + for (i = GALAXY_CONFIG_SIZE; i; i--) { + u8 tmp = ioread8(galaxy->config_port + i - 1); + galaxy->config = (galaxy->config << 8) | tmp; + } + config |= galaxy->config & GALAXY_CONFIG_MASK; + galaxy_set_config(galaxy, config); +} + +static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config) +{ + int err; + + err = wss_detect(galaxy->wss_port); + if (err < 0) + return err; + + wss_set_config(galaxy->wss_port, wss_config); + + err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS); + if (err < 0) + return err; + + return 0; +} + +static void snd_galaxy_free(struct snd_card *card) +{ + struct snd_galaxy *galaxy = card->private_data; + + if (galaxy->wss_port) { + wss_set_config(galaxy->wss_port, 0); + ioport_unmap(galaxy->wss_port); + release_and_free_resource(galaxy->res_wss_port); + } + if (galaxy->config_port) { + galaxy_set_config(galaxy, galaxy->config); + ioport_unmap(galaxy->config_port); + release_and_free_resource(galaxy->res_config_port); + } + if (galaxy->port) { + ioport_unmap(galaxy->port); + release_and_free_resource(galaxy->res_port); + } +} + +static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n) +{ + struct snd_galaxy *galaxy; + struct snd_wss *chip; + struct snd_card *card; + u8 type; + int err; + + err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy, + &card); + if (err < 0) + return err; + + snd_card_set_dev(card, dev); + + card->private_free = snd_galaxy_free; + galaxy = card->private_data; + + galaxy->res_port = request_region(port[n], 16, DRV_NAME); + if (!galaxy->res_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n], + port[n] + 15); + err = -EBUSY; + goto error; + } + galaxy->port = ioport_map(port[n], 16); + + err = galaxy_init(galaxy, &type); + if (err < 0) { + dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]); + goto error; + } + dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]); + + galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG, + 16, DRV_NAME); + if (!galaxy->res_config_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", + port[n] + GALAXY_PORT_CONFIG, + port[n] + GALAXY_PORT_CONFIG + 15); + err = -EBUSY; + goto error; + } + galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16); + + galaxy_config(galaxy, config[n]); + + galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME); + if (!galaxy->res_wss_port) { + dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n], + wss_port[n] + 3); + err = -EBUSY; + goto error; + } + galaxy->wss_port = ioport_map(wss_port[n], 4); + + err = galaxy_wss_config(galaxy, wss_config[n]); + if (err < 0) { + dev_err(dev, "could not configure WSS\n"); + goto error; + } + + strcpy(card->driver, DRV_NAME); + strcpy(card->shortname, DRV_NAME); + sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", + card->shortname, port[n], wss_port[n], irq[n], dma1[n], + dma2[n]); + + err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n], + dma2[n], WSS_HW_DETECT, 0, &chip); + if (err < 0) + goto error; + + err = snd_wss_pcm(chip, 0, NULL); + if (err < 0) + goto error; + + err = snd_wss_mixer(chip); + if (err < 0) + goto error; + + err = snd_wss_timer(chip, 0, NULL); + if (err < 0) + goto error; + + if (mpu_port[n] >= 0) { + err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port[n], 0, mpu_irq[n], + IRQF_DISABLED, NULL); + if (err < 0) + goto error; + } + + if (fm_port[n] >= 0) { + struct snd_opl3 *opl3; + + err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2, + OPL3_HW_AUTO, 0, &opl3); + if (err < 0) { + dev_err(dev, "no OPL device at %#lx\n", fm_port[n]); + goto error; + } + err = snd_opl3_timer_new(opl3, 1, 2); + if (err < 0) + goto error; + + err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); + if (err < 0) + goto error; + } + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(dev, card); + return 0; + +error: + snd_card_free(card); + return err; +} + +static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n) +{ + snd_card_free(dev_get_drvdata(dev)); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct isa_driver snd_galaxy_driver = { + .match = snd_galaxy_match, + .probe = snd_galaxy_probe, + .remove = __devexit_p(snd_galaxy_remove), + + .driver = { + .name = DEV_NAME + } +}; + +static int __init alsa_card_galaxy_init(void) +{ + return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS); +} + +static void __exit alsa_card_galaxy_exit(void) +{ + isa_unregister_driver(&snd_galaxy_driver); +} + +module_init(alsa_card_galaxy_init); +module_exit(alsa_card_galaxy_exit); diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index f26eac8d811..3e4a58b7291 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -191,7 +191,7 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip) static void snd_gusmax_free(struct snd_card *card) { - struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; + struct snd_gusmax *maxcard = card->private_data; if (maxcard == NULL) return; @@ -219,7 +219,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev) if (err < 0) return err; card->private_free = snd_gusmax_free; - maxcard = (struct snd_gusmax *)card->private_data; + maxcard = card->private_data; maxcard->card = card; maxcard->irq = -1; diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 5f3e68401f9..91d6023a63e 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 81284a8fa0c..2259e3f726a 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -72,7 +72,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id) static void snd_sb8_free(struct snd_card *card) { - struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; + struct snd_sb8 *acard = card->private_data; if (acard == NULL) return; diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c deleted file mode 100644 index 6fe27b9d944..00000000000 --- a/sound/isa/sgalaxy.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Driver for Aztech Sound Galaxy cards - * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk. - * - * I don't have documentation for this card, I based this driver on the - * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/init.h> -#include <linux/err.h> -#include <linux/isa.h> -#include <linux/delay.h> -#include <linux/time.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <asm/dma.h> -#include <sound/core.h> -#include <sound/sb.h> -#include <sound/wss.h> -#include <sound/control.h> -#define SNDRV_LEGACY_FIND_FREE_IRQ -#define SNDRV_LEGACY_FIND_FREE_DMA -#include <sound/initval.h> - -MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>"); -MODULE_DESCRIPTION("Aztech Sound Galaxy"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ -static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ -static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ -static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ -static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); -module_param_array(sbport, long, NULL, 0444); -MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); -module_param_array(wssport, long, NULL, 0444); -MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); -module_param_array(dma1, int, NULL, 0444); -MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); - -#define SGALAXY_AUXC_LEFT 18 -#define SGALAXY_AUXC_RIGHT 19 - -#define PFX "sgalaxy: " - -/* - - */ - -#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) - -/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */ -/* short time we actually need it.. */ - -static int snd_sgalaxy_sbdsp_reset(unsigned long port) -{ - int i; - - outb(1, SBP1(port, RESET)); - udelay(10); - outb(0, SBP1(port, RESET)); - udelay(30); - for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); - if (inb(SBP1(port, READ)) != 0xaa) { - snd_printd("sb_reset: failed at 0x%lx!!!\n", port); - return -ENODEV; - } - return 0; -} - -static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port, - unsigned char val) -{ - int i; - - for (i = 10000; i; i--) - if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { - outb(val, SBP1(port, COMMAND)); - return 1; - } - - return 0; -} - -static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id) -{ - return IRQ_NONE; -} - -static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) -{ - static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, - 0x10, 0x18, 0x20, -1, -1, -1, -1}; - static int dma_bits[] = {1, 2, 0, 3}; - int tmp, tmp1; - - if ((tmp = inb(port + 3)) == 0xff) - { - snd_printdd("I/O address dead (0x%lx)\n", port); - return 0; - } -#if 0 - snd_printdd("WSS signature = 0x%x\n", tmp); -#endif - - if ((tmp & 0x3f) != 0x04 && - (tmp & 0x3f) != 0x0f && - (tmp & 0x3f) != 0x00) { - snd_printdd("No WSS signature detected on port 0x%lx\n", - port + 3); - return 0; - } - -#if 0 - snd_printdd(PFX "setting up IRQ/DMA for WSS\n"); -#endif - - /* initialize IRQ for WSS codec */ - tmp = interrupt_bits[irq % 16]; - if (tmp < 0) - return -EINVAL; - - if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) { - snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); - return -EIO; - } - - outb(tmp | 0x40, port); - tmp1 = dma_bits[dma % 4]; - outb(tmp | tmp1, port); - - free_irq(irq, NULL); - - return 0; -} - -static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma) -{ -#if 0 - snd_printdd(PFX "switching to WSS mode\n"); -#endif - - /* switch to WSS mode */ - snd_sgalaxy_sbdsp_reset(sbport[dev]); - - snd_sgalaxy_sbdsp_command(sbport[dev], 9); - snd_sgalaxy_sbdsp_command(sbport[dev], 0); - - udelay(400); - return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); -} - -static struct snd_kcontrol_new snd_sgalaxy_controls[] = { -WSS_DOUBLE("Aux Playback Switch", 0, - SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), -WSS_DOUBLE("Aux Playback Volume", 0, - SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) -}; - -static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip) -{ - struct snd_card *card = chip->card; - struct snd_ctl_elem_id id1, id2; - unsigned int idx; - int err; - - memset(&id1, 0, sizeof(id1)); - memset(&id2, 0, sizeof(id2)); - id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - /* reassign AUX0 to LINE */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Line Playback Switch"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Line Playback Volume"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - /* reassign AUX1 to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); - if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) - return err; - /* build AUX2 input */ - for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_sgalaxy_controls[idx], chip)); - if (err < 0) - return err; - } - return 0; -} - -static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev) -{ - if (!enable[dev]) - return 0; - if (sbport[dev] == SNDRV_AUTO_PORT) { - snd_printk(KERN_ERR PFX "specify SB port\n"); - return 0; - } - if (wssport[dev] == SNDRV_AUTO_PORT) { - snd_printk(KERN_ERR PFX "specify WSS port\n"); - return 0; - } - return 1; -} - -static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev) -{ - static int possible_irqs[] = {7, 9, 10, 11, -1}; - static int possible_dmas[] = {1, 3, 0, -1}; - int err, xirq, xdma1; - struct snd_card *card; - struct snd_wss *chip; - - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); - if (err < 0) - return err; - - xirq = irq[dev]; - if (xirq == SNDRV_AUTO_IRQ) { - if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { - snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); - err = -EBUSY; - goto _err; - } - } - xdma1 = dma1[dev]; - if (xdma1 == SNDRV_AUTO_DMA) { - if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { - snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); - err = -EBUSY; - goto _err; - } - } - - if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) - goto _err; - - err = snd_wss_create(card, wssport[dev] + 4, -1, - xirq, xdma1, -1, - WSS_HW_DETECT, 0, &chip); - if (err < 0) - goto _err; - card->private_data = chip; - - err = snd_wss_pcm(chip, 0, NULL); - if (err < 0) { - snd_printdd(PFX "error creating new WSS PCM device\n"); - goto _err; - } - err = snd_wss_mixer(chip); - if (err < 0) { - snd_printdd(PFX "error creating new WSS mixer\n"); - goto _err; - } - if ((err = snd_sgalaxy_mixer(chip)) < 0) { - snd_printdd(PFX "the mixer rewrite failed\n"); - goto _err; - } - - strcpy(card->driver, "Sound Galaxy"); - strcpy(card->shortname, "Sound Galaxy"); - sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", - wssport[dev], xirq, xdma1); - - snd_card_set_dev(card, devptr); - - if ((err = snd_card_register(card)) < 0) - goto _err; - - dev_set_drvdata(devptr, card); - return 0; - - _err: - snd_card_free(card); - return err; -} - -static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev) -{ - snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); - return 0; -} - -#ifdef CONFIG_PM -static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n, - pm_message_t state) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct snd_wss *chip = card->private_data; - - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - chip->suspend(chip); - return 0; -} - -static int snd_sgalaxy_resume(struct device *pdev, unsigned int n) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct snd_wss *chip = card->private_data; - - chip->resume(chip); - snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); - snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]); - - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - return 0; -} -#endif - -#define DEV_NAME "sgalaxy" - -static struct isa_driver snd_sgalaxy_driver = { - .match = snd_sgalaxy_match, - .probe = snd_sgalaxy_probe, - .remove = __devexit_p(snd_sgalaxy_remove), -#ifdef CONFIG_PM - .suspend = snd_sgalaxy_suspend, - .resume = snd_sgalaxy_resume, -#endif - .driver = { - .name = DEV_NAME - }, -}; - -static int __init alsa_card_sgalaxy_init(void) -{ - return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS); -} - -static void __exit alsa_card_sgalaxy_exit(void) -{ - isa_unregister_driver(&snd_sgalaxy_driver); -} - -module_init(alsa_card_sgalaxy_init) -module_exit(alsa_card_sgalaxy_exit) diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index a513651fa14..76c09021807 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -545,11 +545,3 @@ config SOUND_KAHLUA endif # SOUND_OSS -config SOUND_SH_DAC_AUDIO - tristate "SuperH DAC audio support" - depends on CPU_SH3 && HIGH_RES_TIMERS - -config SOUND_SH_DAC_AUDIO_CHANNEL - int "DAC channel" - default "1" - depends on SOUND_SH_DAC_AUDIO diff --git a/sound/oss/Makefile b/sound/oss/Makefile index 567b8a74178..96f14dcd0cd 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_SOUND_OSS) += sound.o # Please leave it as is, cause the link order is significant ! -obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c index 456a1b4d783..854c303264d 100644 --- a/sound/oss/ac97_codec.c +++ b/sound/oss/ac97_codec.c @@ -21,11 +21,8 @@ * ************************************************************************** * - * The Intel Audio Codec '97 specification is available at the Intel - * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ - * - * The specification itself is currently available at: - * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf + * The Intel Audio Codec '97 specification is available at: + * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf * ************************************************************************** * diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c index c6f2621221b..a8f626d99c5 100644 --- a/sound/oss/au1550_ac97.c +++ b/sound/oss/au1550_ac97.c @@ -43,7 +43,6 @@ #include <linux/sound.h> #include <linux/slab.h> #include <linux/soundcard.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -77,6 +76,7 @@ /* Boot options * 0 = no VRA, 1 = use VRA if codec supports it */ +static DEFINE_MUTEX(au1550_ac97_mutex); static int vra = 1; module_param(vra, bool, 0); MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); @@ -171,7 +171,7 @@ au1550_delay(int msec) static u16 rdcodec(struct ac97_codec *codec, u8 addr) { - struct au1550_state *s = (struct au1550_state *)codec->private_data; + struct au1550_state *s = codec->private_data; unsigned long flags; u32 cmd, val; u16 data; @@ -239,7 +239,7 @@ rdcodec(struct ac97_codec *codec, u8 addr) static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) { - struct au1550_state *s = (struct au1550_state *)codec->private_data; + struct au1550_state *s = codec->private_data; unsigned long flags; u32 cmd, val; int i; @@ -798,9 +798,9 @@ au1550_llseek(struct file *file, loff_t offset, int origin) static int au1550_open_mixdev(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); file->private_data = &au1550_state; - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return 0; } @@ -820,13 +820,13 @@ mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, static long au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; struct ac97_codec *codec = s->codec; int ret; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); ret = mixdev_ioctl(codec, cmd, arg); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } @@ -1031,7 +1031,7 @@ copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user) static ssize_t au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; struct dmabuf *db = &s->dma_adc; DECLARE_WAITQUEUE(wait, current); ssize_t ret; @@ -1111,7 +1111,7 @@ out2: static ssize_t au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; struct dmabuf *db = &s->dma_dac; DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; @@ -1211,7 +1211,7 @@ out2: static unsigned int au1550_poll(struct file *file, struct poll_table_struct *wait) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; unsigned long flags; unsigned int mask = 0; @@ -1250,12 +1250,12 @@ au1550_poll(struct file *file, struct poll_table_struct *wait) static int au1550_mmap(struct file *file, struct vm_area_struct *vma) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; struct dmabuf *db; unsigned long size; int ret = 0; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); mutex_lock(&s->sem); if (vma->vm_flags & VM_WRITE) db = &s->dma_dac; @@ -1283,7 +1283,7 @@ au1550_mmap(struct file *file, struct vm_area_struct *vma) db->mapped = 1; out: mutex_unlock(&s->sem); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } @@ -1342,7 +1342,7 @@ dma_count_done(struct dmabuf *db) static int au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; unsigned long flags; audio_buf_info abinfo; count_info cinfo; @@ -1781,9 +1781,9 @@ au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); ret = au1550_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } @@ -1804,7 +1804,7 @@ au1550_open(struct inode *inode, struct file *file) #endif file->private_data = s; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); /* wait for device to become free */ mutex_lock(&s->open_mutex); while (s->open_mode & file->f_mode) { @@ -1861,21 +1861,21 @@ au1550_open(struct inode *inode, struct file *file) out: mutex_unlock(&s->open_mutex); out2: - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return ret; } static int au1550_release(struct inode *inode, struct file *file) { - struct au1550_state *s = (struct au1550_state *)file->private_data; + struct au1550_state *s = file->private_data; - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); if (file->f_mode & FMODE_WRITE) { - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); drain_dac(s, file->f_flags & O_NONBLOCK); - lock_kernel(); + mutex_lock(&au1550_ac97_mutex); } mutex_lock(&s->open_mutex); @@ -1892,7 +1892,7 @@ au1550_release(struct inode *inode, struct file *file) s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); mutex_unlock(&s->open_mutex); wake_up(&s->open_wait); - unlock_kernel(); + mutex_unlock(&au1550_ac97_mutex); return 0; } diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c index 6ecd41abb06..87e2c72651f 100644 --- a/sound/oss/dmasound/dmasound_core.c +++ b/sound/oss/dmasound/dmasound_core.c @@ -181,7 +181,7 @@ #include <linux/init.h> #include <linux/soundcard.h> #include <linux/poll.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <asm/uaccess.h> @@ -194,6 +194,7 @@ * Declarations */ +static DEFINE_MUTEX(dmasound_core_mutex); int dmasound_catchRadius = 0; module_param(dmasound_catchRadius, int, 0); @@ -323,22 +324,22 @@ static struct { static int mixer_open(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (!try_module_get(dmasound.mach.owner)) { - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return -ENODEV; } mixer.busy = 1; - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } static int mixer_release(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); mixer.busy = 0; module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } @@ -370,9 +371,9 @@ static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = mixer_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } @@ -752,9 +753,9 @@ static int sq_open(struct inode *inode, struct file *file) { int rc; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (!try_module_get(dmasound.mach.owner)) { - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return -ENODEV; } @@ -799,11 +800,11 @@ static int sq_open(struct inode *inode, struct file *file) sound_set_format(AFMT_MU_LAW); } #endif - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; out: module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return rc; } @@ -869,7 +870,7 @@ static int sq_release(struct inode *inode, struct file *file) { int rc = 0; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); if (file->f_mode & FMODE_WRITE) { if (write_sq.busy) @@ -900,7 +901,7 @@ static int sq_release(struct inode *inode, struct file *file) write_sq_wake_up(file); /* checks f_mode */ #endif /* blocking open() */ - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return rc; } @@ -1141,9 +1142,9 @@ static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = sq_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } @@ -1257,7 +1258,7 @@ static int state_open(struct inode *inode, struct file *file) int len = 0; int ret; - lock_kernel(); + mutex_lock(&dmasound_core_mutex); ret = -EBUSY; if (state.busy) goto out; @@ -1329,16 +1330,16 @@ printk("dmasound: stat buffer used %d bytes\n", len) ; state.len = len; ret = 0; out: - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return ret; } static int state_release(struct inode *inode, struct file *file) { - lock_kernel(); + mutex_lock(&dmasound_core_mutex); state.busy = 0; module_put(dmasound.mach.owner); - unlock_kernel(); + mutex_unlock(&dmasound_core_mutex); return 0; } diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c index 2e48b17667d..7b5c77b32a9 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; } @@ -1117,6 +1118,7 @@ static const struct file_operations dev_fileops = { .unlocked_ioctl = dev_ioctl, .open = dev_open, .release = dev_release, + .llseek = noop_llseek, }; static int reset_dsp(void) diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index 51a3d381a59..9890cf2066f 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 479e3025a8a..00000000000 --- a/sound/oss/sh_dac_audio.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * sound/oss/sh_dac_audio.c - * - * SH DAC based sound :( - * - * Copyright (C) 2004,2005 Andriy Skulysh - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/linkage.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/sound.h> -#include <linux/smp_lock.h> -#include <linux/soundcard.h> -#include <linux/interrupt.h> -#include <linux/hrtimer.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/irq.h> -#include <asm/delay.h> -#include <asm/clock.h> -#include <cpu/dac.h> -#include <asm/machvec.h> -#include <mach/hp6xx.h> -#include <asm/hd64461.h> - -#define MODNAME "sh_dac_audio" - -#define BUFFER_SIZE 48000 - -static int rate; -static int empty; -static char *data_buffer, *buffer_begin, *buffer_end; -static int in_use, device_major; -static struct hrtimer hrtimer; -static ktime_t wakeups_per_second; - -static void dac_audio_start_timer(void) -{ - hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); -} - -static void dac_audio_stop_timer(void) -{ - hrtimer_cancel(&hrtimer); -} - -static void dac_audio_reset(void) -{ - dac_audio_stop_timer(); - buffer_begin = buffer_end = data_buffer; - empty = 1; -} - -static void dac_audio_sync(void) -{ - while (!empty) - schedule(); -} - -static void dac_audio_start(void) -{ - if (mach_is_hp6xx()) { - u16 v = __raw_readw(HD64461_GPADR); - v &= ~HD64461_GPADR_SPEAKER; - __raw_writew(v, HD64461_GPADR); - } - - sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); -} -static void dac_audio_stop(void) -{ - dac_audio_stop_timer(); - - if (mach_is_hp6xx()) { - u16 v = __raw_readw(HD64461_GPADR); - v |= HD64461_GPADR_SPEAKER; - __raw_writew(v, HD64461_GPADR); - } - - sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); - sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); -} - -static void dac_audio_set_rate(void) -{ - wakeups_per_second = ktime_set(0, 1000000000 / rate); -} - -static int dac_audio_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int val; - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - dac_audio_sync(); - return 0; - - case SNDCTL_DSP_RESET: - dac_audio_reset(); - return 0; - - case SNDCTL_DSP_GETFMTS: - return put_user(AFMT_U8, (int *)arg); - - case SNDCTL_DSP_SETFMT: - return put_user(AFMT_U8, (int *)arg); - - case SNDCTL_DSP_NONBLOCK: - spin_lock(&file->f_lock); - file->f_flags |= O_NONBLOCK; - spin_unlock(&file->f_lock); - return 0; - - case SNDCTL_DSP_GETCAPS: - return 0; - - case SOUND_PCM_WRITE_RATE: - val = *(int *)arg; - if (val > 0) { - rate = val; - dac_audio_set_rate(); - } - return put_user(rate, (int *)arg); - - case SNDCTL_DSP_STEREO: - return put_user(0, (int *)arg); - - case SOUND_PCM_WRITE_CHANNELS: - return put_user(1, (int *)arg); - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_PROFILE: - return -EINVAL; - - case SNDCTL_DSP_GETBLKSIZE: - return put_user(BUFFER_SIZE, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - return 0; - - default: - printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n", - cmd); - return -EINVAL; - } - return -EINVAL; -} - -static long dac_audio_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) -{ - int ret; - - lock_kernel(); - ret = dac_audio_ioctl(file, cmd, arg); - unlock_kernel(); - - return ret; -} - -static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count, - loff_t * ppos) -{ - int free; - int nbytes; - - if (!count) { - dac_audio_sync(); - return 0; - } - - free = buffer_begin - buffer_end; - - if (free < 0) - free += BUFFER_SIZE; - if ((free == 0) && (empty)) - free = BUFFER_SIZE; - if (count > free) - count = free; - if (buffer_begin > buffer_end) { - if (copy_from_user((void *)buffer_end, buf, count)) - return -EFAULT; - - buffer_end += count; - } else { - nbytes = data_buffer + BUFFER_SIZE - buffer_end; - if (nbytes > count) { - if (copy_from_user((void *)buffer_end, buf, count)) - return -EFAULT; - buffer_end += count; - } else { - if (copy_from_user((void *)buffer_end, buf, nbytes)) - return -EFAULT; - if (copy_from_user - ((void *)data_buffer, buf + nbytes, count - nbytes)) - return -EFAULT; - buffer_end = data_buffer + count - nbytes; - } - } - - if (empty) { - empty = 0; - dac_audio_start_timer(); - } - - return count; -} - -static ssize_t dac_audio_read(struct file *file, char *buf, size_t count, - loff_t * ppos) -{ - return -EINVAL; -} - -static int dac_audio_open(struct inode *inode, struct file *file) -{ - if (file->f_mode & FMODE_READ) - return -ENODEV; - - lock_kernel(); - if (in_use) { - unlock_kernel(); - return -EBUSY; - } - - in_use = 1; - - dac_audio_start(); - unlock_kernel(); - return 0; -} - -static int dac_audio_release(struct inode *inode, struct file *file) -{ - dac_audio_sync(); - dac_audio_stop(); - in_use = 0; - - return 0; -} - -const struct file_operations dac_audio_fops = { - .read = dac_audio_read, - .write = dac_audio_write, - .unlocked_ioctl = dac_audio_unlocked_ioctl, - .open = dac_audio_open, - .release = dac_audio_release, -}; - -static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle) -{ - if (!empty) { - sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); - buffer_begin++; - - if (buffer_begin == data_buffer + BUFFER_SIZE) - buffer_begin = data_buffer; - if (buffer_begin == buffer_end) - empty = 1; - } - - if (!empty) - hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); - - return HRTIMER_NORESTART; -} - -static int __init dac_audio_init(void) -{ - if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) { - printk(KERN_ERR "Cannot register dsp device"); - return device_major; - } - - in_use = 0; - - data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); - if (data_buffer == NULL) - return -ENOMEM; - - dac_audio_reset(); - rate = 8000; - dac_audio_set_rate(); - - /* Today: High Resolution Timer driven DAC playback. - * The timer callback gets called once per sample. Ouch. - * - * Future: A much better approach would be to use the - * SH7720 CMT+DMAC+DAC hardware combination like this: - * - Program sample rate using CMT0 or CMT1 - * - Program DMAC to use CMT for timing and output to DAC - * - Play sound using DMAC, let CPU sleep. - * - While at it, rewrite this driver to use ALSA. - */ - - hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer.function = sh_dac_audio_timer; - - return 0; -} - -static void __exit dac_audio_exit(void) -{ - unregister_sound_dsp(device_major); - kfree((void *)data_buffer); -} - -module_init(dac_audio_init); -module_exit(dac_audio_exit); - -MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); -MODULE_DESCRIPTION("SH DAC sound driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c index f0f0c19fbff..48cda6c4c25 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 92aa762ffb7..46c0d03dbec 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -40,7 +40,7 @@ #include <linux/major.h> #include <linux/delay.h> #include <linux/proc_fs.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/device.h> @@ -56,6 +56,7 @@ * Table for permanently allocated memory (used when unloading the module) */ void * sound_mem_blocks[MAX_MEM_BLOCKS]; +static DEFINE_MUTEX(soundcard_mutex); int sound_nblocks = 0; /* Persistent DMA buffers */ @@ -151,7 +152,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof * big one anyway, we might as well bandage here.. */ - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { @@ -169,7 +170,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof case SND_DEV_MIDIN: ret = MIDIbuf_read(dev, file, buf, count); } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -178,7 +179,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou int dev = iminor(file->f_path.dentry->d_inode); int ret = -EINVAL; - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { case SND_DEV_SEQ: @@ -196,7 +197,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou ret = MIDIbuf_write(dev, file, buf, count); break; } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -210,7 +211,7 @@ static int sound_open(struct inode *inode, struct file *file) printk(KERN_ERR "Invalid minor device %d\n", dev); return -ENXIO; } - lock_kernel(); + mutex_lock(&soundcard_mutex); switch (dev & 0x0f) { case SND_DEV_CTL: dev >>= 4; @@ -247,15 +248,15 @@ static int sound_open(struct inode *inode, struct file *file) retval = -ENXIO; } - unlock_kernel(); - return 0; + mutex_unlock(&soundcard_mutex); + return retval; } static int sound_release(struct inode *inode, struct file *file) { int dev = iminor(inode); - lock_kernel(); + mutex_lock(&soundcard_mutex); DEB(printk("sound_release(dev=%d)\n", dev)); switch (dev & 0x0f) { case SND_DEV_CTL: @@ -280,7 +281,7 @@ static int sound_release(struct inode *inode, struct file *file) default: printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return 0; } @@ -354,7 +355,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cmd == OSS_GETVERSION) return __put_user(SOUND_VERSION, (int __user *)p); - lock_kernel(); + mutex_lock(&soundcard_mutex); if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ (dev & 0x0f) != SND_DEV_CTL) { dtype = dev & 0x0f; @@ -369,7 +370,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = sound_mixer_ioctl(dev >> 4, cmd, p); break; } - unlock_kernel(); + mutex_unlock(&soundcard_mutex); return ret; } @@ -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 b15840ad252..44357d877a2 100644 --- a/sound/oss/swarm_cs4297a.c +++ b/sound/oss/swarm_cs4297a.c @@ -68,7 +68,6 @@ #include <linux/delay.h> #include <linux/sound.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/soundcard.h> #include <linux/ac97_codec.h> #include <linux/pci.h> @@ -94,6 +93,7 @@ struct cs4297a_state; +static DEFINE_MUTEX(swarm_cs4297a_mutex); static void stop_dac(struct cs4297a_state *s); static void stop_adc(struct cs4297a_state *s); static void start_dac(struct cs4297a_state *s); @@ -1535,7 +1535,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n")); - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); list_for_each(entry, &cs4297a_devs) { s = list_entry(entry, struct cs4297a_state, list); @@ -1547,7 +1547,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n")); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return -ENODEV; } VALIDATE_STATE(s); @@ -1555,7 +1555,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file) CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n")); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return nonseekable_open(inode, file); } @@ -1575,10 +1575,10 @@ static int cs4297a_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd, arg); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } @@ -2350,9 +2350,9 @@ static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = cs4297a_ioctl(file, cmd, arg); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } @@ -2509,9 +2509,9 @@ static int cs4297a_open(struct inode *inode, struct file *file) { int ret; - lock_kernel(); + mutex_lock(&swarm_cs4297a_mutex); ret = cs4297a_open(inode, file); - unlock_kernel(); + mutex_unlock(&swarm_cs4297a_mutex); return ret; } diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c index 8cd73cdd88a..643f1113b1d 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -145,7 +145,6 @@ #include <linux/init.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <linux/wait.h> #include <linux/interrupt.h> #include <linux/mutex.h> @@ -160,6 +159,7 @@ #ifdef VWSND_DEBUG +static DEFINE_MUTEX(vwsnd_mutex); static int shut_up = 1; /* @@ -2891,11 +2891,11 @@ static long vwsnd_audio_ioctl(struct file *file, vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; int ret; - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->io_mutex); ret = vwsnd_audio_do_ioctl(file, cmd, arg); mutex_unlock(&devc->io_mutex); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return ret; } @@ -2922,7 +2922,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) DBGE("(inode=0x%p, file=0x%p)\n", inode, file); - lock_kernel(); + mutex_lock(&vwsnd_mutex); INC_USE_COUNT; for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) @@ -2930,7 +2930,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) if (devc == NULL) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ENODEV; } @@ -2939,13 +2939,13 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) mutex_unlock(&devc->open_mutex); if (file->f_flags & O_NONBLOCK) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -EBUSY; } interruptible_sleep_on(&devc->open_wait); if (signal_pending(current)) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ERESTARTSYS; } mutex_lock(&devc->open_mutex); @@ -2998,7 +2998,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file) file->private_data = devc; DBGRV(); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return 0; } @@ -3012,7 +3012,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file) vwsnd_port_t *wport = NULL, *rport = NULL; int err = 0; - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->io_mutex); { DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); @@ -3040,7 +3040,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file) wake_up(&devc->open_wait); DEC_USE_COUNT; DBGR(); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return err; } @@ -3068,18 +3068,18 @@ static int vwsnd_mixer_open(struct inode *inode, struct file *file) DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); INC_USE_COUNT; - lock_kernel(); + mutex_lock(&vwsnd_mutex); for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) if (devc->mixer_minor == iminor(inode)) break; if (devc == NULL) { DEC_USE_COUNT; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return -ENODEV; } file->private_data = devc; - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return 0; } @@ -3223,7 +3223,7 @@ static long vwsnd_mixer_ioctl(struct file *file, DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); - lock_kernel(); + mutex_lock(&vwsnd_mutex); mutex_lock(&devc->mix_mutex); { if ((cmd & ~nrmask) == MIXER_READ(0)) @@ -3234,7 +3234,7 @@ static long vwsnd_mixer_ioctl(struct file *file, retval = -EINVAL; } mutex_unlock(&devc->mix_mutex); - unlock_kernel(); + mutex_unlock(&vwsnd_mutex); return retval; } diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index e7a8cd058ef..12e34653b8a 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -207,12 +207,12 @@ config SND_CMIPCI config SND_OXYGEN_LIB tristate - select SND_PCM - select SND_MPU401_UART config SND_OXYGEN tristate "C-Media 8788 (Oxygen)" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART help Say Y here to include support for sound cards based on the C-Media CMI8788 (Oxygen HD Audio) chip: @@ -581,6 +581,8 @@ config SND_HDSPM config SND_HIFIER tristate "TempoTec HiFier Fantasia" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART help Say Y here to include support for the MediaTek/TempoTec HiFier Fantasia sound card. @@ -815,14 +817,17 @@ config SND_VIA82XX_MODEM will be called snd-via82xx-modem. config SND_VIRTUOSO - tristate "Asus Virtuoso 100/200 (Xonar)" + tristate "Asus Virtuoso 66/100/200 (Xonar)" select SND_OXYGEN_LIB + select SND_PCM + select SND_MPU401_UART + select SND_JACK if INPUT=y || INPUT=SND help Say Y here to include support for sound cards based on the - Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, + Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), and Essence STX. - Support for the DS is experimental. - Support for the HDAV1.3 (Deluxe) is very experimental. + Support for the HDAV1.3 (Deluxe) is incomplete; for the + HDAV1.3 Slim and Xense, missing. To compile this driver as a module, choose M here: the module will be called snd-virtuoso. diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 3b441344822..22c5fc62553 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 c92f493d341..557c782ae4f 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -23,7 +23,7 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex) if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); - // Intialize AC97 codec stuff. + // Initialize AC97 codec stuff. ac97.private_data = vortex; ac97.scaps = AC97_SCAP_NO_SPDIF; err = snd_ac97_mixer(pbus, &ac97, &vortex->codec); diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index 14b8d9a91aa..f19c1107725 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -670,8 +670,9 @@ struct snd_ca0106_details { gpio_type = 2 -> shared side-out/line-in. */ int i2c_adc; /* with i2c_adc=1, the driver adds some capture volume controls, phone, mic, line-in and aux. */ - int spi_dac; /* spi_dac=1 adds the mute switch for each analog - output, front, rear, etc. */ + u16 spi_dac; /* spi_dac = 0 -> no spi interface for DACs + spi_dac = 0x<front><rear><center-lfe><side> + -> specifies DAC id for each channel pair. */ }; // definition of the chip-specific record diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 0a3d3d6e77b..d2d12c08f93 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -227,7 +227,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "Audigy SE [SB0570]", .gpio_type = 1, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , /* New Audigy LS. Has a different DAC. */ /* SB0570: * CTRL:CA0106-DAT @@ -238,7 +238,17 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "Audigy SE OEM [SB0570a]", .gpio_type = 1, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , + /* Sound Blaster 5.1vx + * Tested: Playback on front, rear, center/lfe speakers + * Not-Tested: Capture + */ + { .serial = 0x10041102, + .name = "Sound Blaster 5.1vx [SB1070]", + .gpio_type = 1, + .i2c_adc = 0, + .spi_dac = 0x0124 + } , /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ /* SB0438 * CTRL:CA0106-DAT @@ -254,7 +264,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .name = "MSI K8N Diamond MB", .gpio_type = 2, .i2c_adc = 1, - .spi_dac = 1 } , + .spi_dac = 0x4021 } , /* Giga-byte GA-G1975X mobo * Novell bnc#395807 */ @@ -483,16 +493,18 @@ static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime) } static const int spi_dacd_reg[] = { - [PCM_FRONT_CHANNEL] = SPI_DACD4_REG, - [PCM_REAR_CHANNEL] = SPI_DACD0_REG, - [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG, - [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_REG, + SPI_DACD0_REG, + SPI_DACD1_REG, + SPI_DACD2_REG, + 0, + SPI_DACD4_REG, }; static const int spi_dacd_bit[] = { - [PCM_FRONT_CHANNEL] = SPI_DACD4_BIT, - [PCM_REAR_CHANNEL] = SPI_DACD0_BIT, - [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT, - [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT, + SPI_DACD0_BIT, + SPI_DACD1_BIT, + SPI_DACD2_BIT, + 0, + SPI_DACD4_BIT, }; static void restore_spdif_bits(struct snd_ca0106 *chip, int idx) @@ -504,6 +516,45 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx) } } +static int snd_ca0106_channel_dac(struct snd_ca0106_details *details, + int channel_id) +{ + switch (channel_id) { + case PCM_FRONT_CHANNEL: + return (details->spi_dac & 0xf000) >> (4 * 3); + case PCM_REAR_CHANNEL: + return (details->spi_dac & 0x0f00) >> (4 * 2); + case PCM_CENTER_LFE_CHANNEL: + return (details->spi_dac & 0x00f0) >> (4 * 1); + case PCM_UNKNOWN_CHANNEL: + return (details->spi_dac & 0x000f) >> (4 * 0); + default: + snd_printk(KERN_DEBUG "ca0106: unknown channel_id %d\n", + channel_id); + } + return 0; +} + +static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id, + int power) +{ + if (chip->details->spi_dac) { + const int dac = snd_ca0106_channel_dac(chip->details, + channel_id); + const int reg = spi_dacd_reg[dac]; + const int bit = spi_dacd_bit[dac]; + + if (power) + /* Power up */ + chip->spi_dac_reg[reg] &= ~bit; + else + /* Power down */ + chip->spi_dac_reg[reg] |= bit; + return snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + } + return 0; +} + /* open_playback callback */ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id) @@ -543,12 +594,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr return err; snd_pcm_set_sync(substream); - if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) { - const int reg = spi_dacd_reg[channel_id]; - - /* Power up dac */ - chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id]; - err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + /* Front channel dac should already be on */ + if (channel_id != PCM_FRONT_CHANNEL) { + err = snd_ca0106_pcm_power_dac(chip, channel_id, 1); if (err < 0) return err; } @@ -568,13 +616,14 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream) restore_spdif_bits(chip, epcm->channel_id); - if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) { - const int reg = spi_dacd_reg[epcm->channel_id]; - - /* Power down DAC */ - chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id]; - snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + /* Front channel dac should stay on */ + if (epcm->channel_id != PCM_FRONT_CHANNEL) { + int err; + err = snd_ca0106_pcm_power_dac(chip, epcm->channel_id, 0); + if (err < 0) + return err; } + /* FIXME: maybe zero others */ return 0; } @@ -1002,29 +1051,27 @@ snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream) struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ca0106_pcm *epcm = runtime->private_data; - snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0; + unsigned int ptr, prev_ptr; int channel = epcm->channel_id; + int timeout = 10; if (!epcm->running) return 0; - ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); - ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); - ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); - if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); - ptr2 = bytes_to_frames(runtime, ptr1); - ptr2+= (ptr4 >> 3) * runtime->period_size; - ptr=ptr2; - if (ptr >= runtime->buffer_size) - ptr -= runtime->buffer_size; - /* - printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, " - "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", - ptr1, ptr2, ptr, (int)runtime->buffer_size, - (int)runtime->period_size, (int)runtime->frame_bits, - (int)runtime->rate); - */ - return ptr; + prev_ptr = -1; + do { + ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); + ptr = (ptr >> 3) * runtime->period_size; + ptr += bytes_to_frames(runtime, + snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel)); + if (ptr >= runtime->buffer_size) + ptr -= runtime->buffer_size; + if (prev_ptr == ptr) + return ptr; + prev_ptr = ptr; + } while (--timeout); + snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n"); + return 0; } /* pointer_capture callback */ @@ -1362,7 +1409,7 @@ static unsigned int spi_dac_init[] = { SPI_REG(12, 0x00), SPI_REG(SPI_LDA4_REG, SPI_DA_BIT_0dB), SPI_REG(SPI_RDA4_REG, SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE), - SPI_REG(SPI_DACD4_REG, 0x00), + SPI_REG(SPI_DACD4_REG, SPI_DACD4_BIT), }; static unsigned int i2c_adc_init[][2] = { @@ -1541,7 +1588,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) /* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */ } - if (chip->details->spi_dac == 1) { + if (chip->details->spi_dac) { /* The SB0570 use SPI to control DAC. */ int size, n; @@ -1553,6 +1600,9 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) if (reg < ARRAY_SIZE(chip->spi_dac_reg)) chip->spi_dac_reg[reg] = spi_dac_init[n]; } + + /* Enable front dac only */ + snd_ca0106_pcm_power_dac(chip, PCM_FRONT_CHANNEL, 1); } } diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 85fd315d999..630aa499818 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -676,28 +676,65 @@ static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = I2C_VOLUME("Aux Capture Volume", 3), }; -#define SPI_SWITCH(xname,reg,bit) \ -{ \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = spi_mute_info, \ - .get = spi_mute_get, \ - .put = spi_mute_put, \ - .private_value = (reg<<SPI_REG_SHIFT) | (bit) \ -} - -static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[] -__devinitdata = { - SPI_SWITCH("Analog Front Playback Switch", - SPI_DMUTE4_REG, SPI_DMUTE4_BIT), - SPI_SWITCH("Analog Rear Playback Switch", - SPI_DMUTE0_REG, SPI_DMUTE0_BIT), - SPI_SWITCH("Analog Center/LFE Playback Switch", - SPI_DMUTE2_REG, SPI_DMUTE2_BIT), - SPI_SWITCH("Analog Side Playback Switch", - SPI_DMUTE1_REG, SPI_DMUTE1_BIT), +static const int spi_dmute_reg[] = { + SPI_DMUTE0_REG, + SPI_DMUTE1_REG, + SPI_DMUTE2_REG, + 0, + SPI_DMUTE4_REG, +}; +static const int spi_dmute_bit[] = { + SPI_DMUTE0_BIT, + SPI_DMUTE1_BIT, + SPI_DMUTE2_BIT, + 0, + SPI_DMUTE4_BIT, }; +static struct snd_kcontrol_new __devinit +snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details, + int channel_id) +{ + struct snd_kcontrol_new spi_switch = {0}; + int reg, bit; + int dac_id; + + spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + spi_switch.info = spi_mute_info; + spi_switch.get = spi_mute_get; + spi_switch.put = spi_mute_put; + + switch (channel_id) { + case PCM_FRONT_CHANNEL: + spi_switch.name = "Analog Front Playback Switch"; + dac_id = (details->spi_dac & 0xf000) >> (4 * 3); + break; + case PCM_REAR_CHANNEL: + spi_switch.name = "Analog Rear Playback Switch"; + dac_id = (details->spi_dac & 0x0f00) >> (4 * 2); + break; + case PCM_CENTER_LFE_CHANNEL: + spi_switch.name = "Analog Center/LFE Playback Switch"; + dac_id = (details->spi_dac & 0x00f0) >> (4 * 1); + break; + case PCM_UNKNOWN_CHANNEL: + spi_switch.name = "Analog Side Playback Switch"; + dac_id = (details->spi_dac & 0x000f) >> (4 * 0); + break; + default: + /* Unused channel */ + spi_switch.name = NULL; + dac_id = 0; + } + reg = spi_dmute_reg[dac_id]; + bit = spi_dmute_bit[dac_id]; + + spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit; + + return spi_switch; +} + static int __devinit remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; @@ -832,8 +869,18 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) if (err < 0) return err; } - if (emu->details->spi_dac == 1) - ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); + if (emu->details->spi_dac) { + int i; + for (i = 0;; i++) { + struct snd_kcontrol_new ctl; + ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i); + if (!ctl.name) + break; + err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu)); + if (err < 0) + return err; + } + } /* Create virtual master controls */ vmaster = snd_ctl_make_virtual_master("Master Playback Volume", @@ -845,7 +892,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) return err; add_slaves(card, vmaster, slave_vols); - if (emu->details->spi_dac == 1) { + if (emu->details->spi_dac) { vmaster = snd_ctl_make_virtual_master("Master Playback Switch", NULL); if (!vmaster) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 4203782d7cb..aff8387c45c 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 8578c70c61f..bab564824ef 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -321,7 +321,7 @@ static struct snd_rawmidi_ops snd_emu10k1_midi_input = static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi) { - struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data; + struct snd_emu10k1_midi *midi = rmidi->private_data; midi->interrupt = NULL; midi->rmidi = NULL; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 55b83ef73c6..622bace148e 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 ffb1ddb8dc2..957a311514c 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/ens1370.c b/sound/pci/ens1370.c index c7fba537981..537cfba829a 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -22,7 +22,7 @@ /* Power-Management-Code ( CONFIG_PM ) * for ens1371 only ( FIXME ) * derived from cs4281.c, atiixp.c and via82xx.c - * using http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c1540.htm + * using http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/ * by Kurt J. Bosch */ diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 9194c3c1d04..0ea5cc60ac7 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 24bc195b02d..17ef3658f34 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 dd8fb86c842..644e3f14f8c 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 4303353feda..fdf8d44f8b6 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 803b298f741..cb0c23a6b47 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 5ea21285ee1..fb0582f8d72 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 1053fff4bd0..21aa9b0e28f 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 28ab4aead48..46bbefe2e4a 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 b697fd2a6f8..f7ff3f7ccb8 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 fb684f00156..00000000000 --- 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 af478019088..46c8bf48c31 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 4ef5efaaaef..460fb2ef7e3 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 31b5d9eeba6..6361f752b5f 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 2bc0f07cf33..d3e49aa5b9e 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 d382d3c81c0..00000000000 --- 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 f636870dc71..00000000000 --- 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 2cd1ae809e4..5f00589cb79 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 f3f861bd1bf..93fa59cc60e 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 ae3acb2b42d..d1c3f8defc4 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 d216362626d..712c1710f9a 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -563,6 +563,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_MEDIASTATION: + case ICE1712_SUBDEVICE_EDIROLDA2496: ice->num_total_dacs = 8; ice->num_total_adcs = 8; break; @@ -635,6 +636,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice); break; case ICE1712_SUBDEVICE_DELTA1010LT: + case ICE1712_SUBDEVICE_EDIROLDA2496: err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice); break; case ICE1712_SUBDEVICE_DELTA66: @@ -734,6 +736,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA66: case ICE1712_SUBDEVICE_VX442: case ICE1712_SUBDEVICE_DELTA66E: + case ICE1712_SUBDEVICE_EDIROLDA2496: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; @@ -813,5 +816,12 @@ struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { .chip_init = snd_ice1712_delta_init, .build_controls = snd_ice1712_delta_add_controls, }, + { + .subvendor = ICE1712_SUBDEVICE_EDIROLDA2496, + .name = "Edirol DA2496", + .model = "da2496", + .chip_init = snd_ice1712_delta_init, + .build_controls = snd_ice1712_delta_add_controls, + }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index f7f14df81f2..1a0ac6cd650 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -34,7 +34,8 @@ "{MidiMan M Audio,Delta 410},"\ "{MidiMan M Audio,Audiophile 24/96},"\ "{Digigram,VX442},"\ - "{Lionstracs,Mediastation}," + "{Lionstracs,Mediastation},"\ + "{Edirol,DA2496}," #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 #define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 @@ -47,6 +48,7 @@ #define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 #define ICE1712_SUBDEVICE_VX442 0x12143cd6 #define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100 +#define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010 /* entry point */ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 6bc3f91b728..cdb873f5da5 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -638,7 +638,7 @@ static struct snd_kcontrol_new pontis_controls[] __devinitdata = { */ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; char line[64]; unsigned int reg, val; mutex_lock(&ice->gpio_mutex); @@ -653,7 +653,7 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; mutex_lock(&ice->gpio_mutex); @@ -676,7 +676,7 @@ static void wm_proc_init(struct snd_ice1712 *ice) static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; mutex_lock(&ice->gpio_mutex); diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 2a8e5cd8f2d..e36ddb94c38 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -654,7 +654,7 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) static void stac9460_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + struct snd_ice1712 *ice = entry->private_data; int reg, val; /* registers 0x0 - 0x14 */ for (reg = 0; reg <= 0x15; reg++) { diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6433e65c950..400f9ebd243 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -716,7 +716,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich * Intel 82443MX running a 100MHz processor system bus has a hardware bug, * which aborts PCI busmaster for audio transfer. A workaround is to set * the pages as non-cached. For details, see the errata in - * http://www.intel.com/design/chipsets/specupdt/245051.htm + * http://download.intel.com/design/chipsets/specupdt/24505108.pdf */ static void fill_nocache(void *buf, int size, int nocache) { @@ -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 289cb4dacfc..98a8eb3c92f 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -79,6 +79,7 @@ static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = { { OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF }, + { OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN }, @@ -505,7 +506,8 @@ static const struct oxygen_model model_generic = { PLAYBACK_2_TO_AC97_1 | CAPTURE_0_FROM_I2S_1 | CAPTURE_1_FROM_SPDIF | - CAPTURE_2_FROM_AC97_1, + CAPTURE_2_FROM_AC97_1 | + AC97_CD_INPUT, .dac_channels = 8, .dac_volume_min = 0, .dac_volume_max = 255, @@ -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 6147216af74..7d5222caa0a 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -34,6 +34,7 @@ /* CAPTURE_3_FROM_I2S_3 not implemented */ #define MIDI_OUTPUT 0x0800 #define MIDI_INPUT 0x1000 +#define AC97_CD_INPUT 0x2000 enum { CONTROL_SPDIF_PCM, @@ -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 fad03d64e3a..e5ebe56fb0c 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -308,25 +308,46 @@ static void oxygen_restore_eeprom(struct oxygen *chip, } } -static void pci_bridge_magic(void) +static void configure_pcie_bridge(struct pci_dev *pci) { - struct pci_dev *pci = NULL; + enum { PEX811X, PI7C9X110 }; + static const struct pci_device_id bridge_ids[] = { + { PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X }, + { PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X }, + { PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 }, + { } + }; + struct pci_dev *bridge; + const struct pci_device_id *id; u32 tmp; - for (;;) { - /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */ - pci = pci_get_device(0x12d8, 0xe110, pci); - if (!pci) - break; - /* - * ... configure its secondary internal arbiter to park to - * the secondary port, instead of to the last master. - */ - if (!pci_read_config_dword(pci, 0x40, &tmp)) { - tmp |= 1; - pci_write_config_dword(pci, 0x40, tmp); - } - /* Why? Try asking C-Media. */ + if (!pci->bus || !pci->bus->self) + return; + bridge = pci->bus->self; + + id = pci_match_id(bridge_ids, bridge); + if (!id) + return; + + switch (id->driver_data) { + case PEX811X: /* PLX PEX8111/PEX8112 PCIe/PCI bridge */ + pci_read_config_dword(bridge, 0x48, &tmp); + tmp |= 1; /* enable blind prefetching */ + tmp |= 1 << 11; /* enable beacon generation */ + pci_write_config_dword(bridge, 0x48, tmp); + + pci_write_config_dword(bridge, 0x84, 0x0c); + pci_read_config_dword(bridge, 0x88, &tmp); + tmp &= ~(7 << 27); + tmp |= 2 << 27; /* set prefetch size to 128 bytes */ + pci_write_config_dword(bridge, 0x88, tmp); + break; + + case PI7C9X110: /* Pericom PI7C9X110 PCIe/PCI bridge */ + pci_read_config_dword(bridge, 0x40, &tmp); + tmp |= 1; /* park the PCI arbiter to the sound chip */ + pci_write_config_dword(bridge, 0x40, tmp); + break; } } @@ -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 f375b8a2786..2849b36f5f7 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -708,7 +708,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | ((stereo) << 16) | (index), \ } -static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); +static DECLARE_TLV_DB_SCALE(monitor_db_scale, -600, 600, 0); static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); @@ -972,6 +972,9 @@ static int add_controls(struct oxygen *chip, if (!strcmp(template.name, "Stereo Upmixing") && chip->model.dac_channels == 2) continue; + if (!strncmp(template.name, "CD Capture ", 11) && + !(chip->model.device_config & AC97_CD_INPUT)) + continue; if (!strcmp(template.name, "Master Playback Volume") && chip->model.dac_tlv) { template.tlv.p = chip->model.dac_tlv; diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 9dff6954c39..814667442eb 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -56,8 +56,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = { .channels_max = 2, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; static const struct snd_pcm_hardware oxygen_multichannel_hardware = { @@ -82,8 +82,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = { .channels_max = 8, .buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX_MULTICH, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN, }; static const struct snd_pcm_hardware oxygen_ac97_hardware = { @@ -100,8 +100,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = { .channels_max = 2, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX / 2, - .periods_min = 2, + .period_bytes_max = BUFFER_BYTES_MAX, + .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 72de159d456..4dcd41b7825 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -436,13 +436,15 @@ /* OXYGEN_CHANNEL_* */ #define OXYGEN_CODEC_VERSION 0xe4 -#define OXYGEN_XCID_MASK 0x07 +#define OXYGEN_CODEC_ID_MASK 0x07 #define OXYGEN_REVISION 0xe6 -#define OXYGEN_REVISION_XPKGID_MASK 0x0007 +#define OXYGEN_PACKAGE_ID_MASK 0x0007 +#define OXYGEN_PACKAGE_ID_8786 0x0004 +#define OXYGEN_PACKAGE_ID_8787 0x0006 +#define OXYGEN_PACKAGE_ID_8788 0x0007 #define OXYGEN_REVISION_MASK 0xfff8 -#define OXYGEN_REVISION_2 0x0008 /* bit flag */ -#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */ +#define OXYGEN_REVISION_2 0x0008 #define OXYGEN_OFFSIN_48K 0xe8 #define OXYGEN_OFFSBASE_48K 0xe9 diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index f03a2f2cffe..469010a8b84 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -25,9 +25,9 @@ #include "xonar.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_DESCRIPTION("Asus AVx00 driver"); +MODULE_DESCRIPTION("Asus Virtuoso driver"); MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -49,6 +49,7 @@ static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = { { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835e) }, { OXYGEN_PCI_SUBID(0x1043, 0x838e) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } @@ -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 7c4986b27f2..aa27c31049a 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -367,13 +367,6 @@ static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip, static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); -static int xonar_d1_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - static int xonar_d1_mixer_init(struct oxygen *chip) { int err; @@ -391,7 +384,6 @@ static const struct oxygen_model model_xonar_d1 = { .longname = "Asus Virtuoso 100", .chip = "AV200", .init = xonar_d1_init, - .control_filter = xonar_d1_control_filter, .mixer_init = xonar_d1_mixer_init, .cleanup = xonar_d1_cleanup, .suspend = xonar_d1_suspend, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index ba18fb546b4..d491fd6c0be 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -132,6 +132,18 @@ * GPIO 5 <- 0 */ +/* + * Xonar HDAV1.3 Slim + * ------------------ + * + * CMI8788: + * + * GPIO 1 -> enable output + * + * TXD -> HDMI controller + * RXD <- HDMI controller + */ + #include <linux/pci.h> #include <linux/delay.h> #include <linux/mutex.h> @@ -362,7 +374,6 @@ static void xonar_st_init_common(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; - data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; data->hp_gain_offset = 2*-18; @@ -408,6 +419,7 @@ static void xonar_st_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + data->generic.anti_pop_delay = 100; data->has_cs2000 = 1; data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; @@ -428,6 +440,7 @@ static void xonar_stx_init(struct oxygen *chip) struct xonar_pcm179x *data = chip->model_data; xonar_st_init_i2c(chip); + data->generic.anti_pop_delay = 800; data->generic.ext_power_reg = OXYGEN_GPI_DATA; data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->generic.ext_power_bit = GPI_EXT_POWER; @@ -915,13 +928,6 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template) return 0; } -static int xonar_st_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - static int add_pcm1796_controls(struct oxygen *chip) { int err; @@ -991,7 +997,8 @@ static const struct oxygen_model model_xonar_d2 = { CAPTURE_0_FROM_I2S_2 | CAPTURE_1_FROM_SPDIF | MIDI_OUTPUT | - MIDI_INPUT, + MIDI_INPUT | + AC97_CD_INPUT, .dac_channels = 8, .dac_volume_min = 255 - 2*60, .dac_volume_max = 255, @@ -1037,7 +1044,6 @@ static const struct oxygen_model model_xonar_st = { .longname = "Asus Virtuoso 100", .chip = "AV200", .init = xonar_st_init, - .control_filter = xonar_st_control_filter, .mixer_init = xonar_st_mixer_init, .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, @@ -1108,6 +1114,9 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model.resume = xonar_stx_resume; chip->model.set_dac_params = set_pcm1796_params; break; + case 0x835e: + snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n"); + return -ENODEV; default: return -EINVAL; } diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index dbc4b89d74e..200f7601276 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -25,16 +25,24 @@ * SPI 0 -> WM8766 (surround, center/LFE, back) * SPI 1 -> WM8776 (front, input) * - * GPIO 4 <- headphone detect - * GPIO 6 -> route input jack to input 1/2 (1/0) - * GPIO 7 -> enable output to speakers - * GPIO 8 -> enable output to speakers + * GPIO 4 <- headphone detect, 0 = plugged + * GPIO 6 -> route input jack to mic-in (0) or line-in (1) + * GPIO 7 -> enable output to front L/R speaker channels + * GPIO 8 -> enable output to other speaker channels and front panel headphone + * + * WM8766: + * + * input 1 <- line + * input 2 <- mic + * input 3 <- front mic + * input 4 <- aux */ #include <linux/pci.h> #include <linux/delay.h> #include <sound/control.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -44,7 +52,8 @@ #define GPIO_DS_HP_DETECT 0x0010 #define GPIO_DS_INPUT_ROUTE 0x0040 -#define GPIO_DS_OUTPUT_ENABLE 0x0180 +#define GPIO_DS_OUTPUT_FRONTLR 0x0080 +#define GPIO_DS_OUTPUT_ENABLE 0x0100 #define LC_CONTROL_LIMITER 0x40000000 #define LC_CONTROL_ALC 0x20000000 @@ -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 f64fb7d988c..ad5202efd7a 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 d19dc052c39..d5f5b440fc4 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1527,14 +1527,14 @@ snd_rme96_free(void *private_data) static void snd_rme96_free_spdif_pcm(struct snd_pcm *pcm) { - struct rme96 *rme96 = (struct rme96 *) pcm->private_data; + struct rme96 *rme96 = pcm->private_data; rme96->spdif_pcm = NULL; } static void snd_rme96_free_adat_pcm(struct snd_pcm *pcm) { - struct rme96 *rme96 = (struct rme96 *) pcm->private_data; + struct rme96 *rme96 = pcm->private_data; rme96->adat_pcm = NULL; } @@ -1661,7 +1661,7 @@ static void snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { int n; - struct rme96 *rme96 = (struct rme96 *)entry->private_data; + struct rme96 *rme96 = entry->private_data; rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); @@ -2348,7 +2348,7 @@ snd_rme96_probe(struct pci_dev *pci, if (err < 0) return err; card->private_free = snd_rme96_card_free; - rme96 = (struct rme96 *)card->private_data; + rme96 = card->private_data; rme96->card = card; rme96->pci = pci; snd_card_set_dev(card, &pci->dev); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index b92adef8e81..0b720cf7783 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3284,7 +3284,7 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp) static void snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct hdsp *hdsp = (struct hdsp *) entry->private_data; + struct hdsp *hdsp = entry->private_data; unsigned int status; unsigned int status2; char *pref_sync_ref; @@ -4566,7 +4566,7 @@ static int hdsp_get_peak(struct hdsp *hdsp, struct hdsp_peak_rms __user *peak_rm static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg) { - struct hdsp *hdsp = (struct hdsp *)hw->private_data; + struct hdsp *hdsp = hw->private_data; void __user *argp = (void __user *)arg; int err; @@ -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 547b713d720..0c98ef9156d 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/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 7ab9174a8a8..8cc4733698a 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -142,10 +142,9 @@ static int snd_pdacf_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; link->resource[0]->end = 16; - link->conf.Attributes = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + link->config_flags = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; + link->config_index = 1; + link->config_regs = PRESENT_OPTION; return pdacf_config(link); } @@ -217,7 +216,8 @@ static int pdacf_config(struct pcmcia_device *link) int ret; snd_printdd(KERN_DEBUG "pdacf_config called\n"); - link->conf.ConfigIndex = 0x5; + link->config_index = 0x5; + link->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; ret = pcmcia_request_io(link); if (ret) @@ -227,7 +227,7 @@ static int pdacf_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -287,9 +287,7 @@ MODULE_DEVICE_TABLE(pcmcia, snd_pdacf_ids); static struct pcmcia_driver pdacf_cs_driver = { .owner = THIS_MODULE, - .drv = { - .name = "snd-pdaudiocf", - }, + .name = "snd-pdaudiocf", .probe = snd_pdacf_probe, .remove = snd_pdacf_detach, .id_table = snd_pdacf_ids, diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h index 5cc3e457307..bd26e092aea 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.h +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <asm/io.h> #include <linux/interrupt.h> -#include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index a6edfc3be29..80000d631f8 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -2,7 +2,7 @@ * Driver for Digigram VXpocket V2/440 soundcards * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> - * + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -162,10 +162,9 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl, link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; link->resource[0]->end = 16; - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + link->config_flags |= CONF_ENABLE_IRQ; + link->config_index = 1; + link->config_regs = PRESENT_OPTION; *chip_ret = vxp; return 0; @@ -234,7 +233,7 @@ static int vxpocket_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -359,9 +358,7 @@ MODULE_DEVICE_TABLE(pcmcia, vxp_ids); static struct pcmcia_driver vxp_cs_driver = { .owner = THIS_MODULE, - .drv = { - .name = "snd-vxpocket", - }, + .name = "snd-vxpocket", .probe = vxpocket_probe, .remove = vxpocket_detach, .id_table = vxp_ids, diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h index d9110669d04..13d658c1a21 100644 --- a/sound/pcmcia/vx/vxpocket.h +++ b/sound/pcmcia/vx/vxpocket.h @@ -23,7 +23,6 @@ #include <sound/vx_core.h> -#include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 2f12da4da56..581a670e826 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 20afdf9772e..961d9829769 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -785,7 +785,7 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix) if (! mix->i2c.client) return -ENODEV; if (mix->capture_source) - mix->acs = mix->acs |= 2; + mix->acs |= 2; else mix->acs &= ~2; return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs); diff --git a/sound/sh/aica.c b/sound/sh/aica.c index a0df401ebb9..94c6ea7fa7c 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -188,7 +188,7 @@ static void spu_reset(void) spu_memset(0, 0, 0x200000 / 4); /* Put ARM7 in endless loop */ local_irq_save(flags); - ctrl_outl(0xea000002, SPU_MEMORY_BASE); + __raw_writel(0xea000002, SPU_MEMORY_BASE); local_irq_restore(flags); spu_enable(); } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ff7b922a0f4..94a9d06b902 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4270 if I2C select SND_SOC_DA7210 if I2C select SND_SOC_JZ4740 if SOC_JZ4740 + select SND_SOC_MAX98088 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 select SND_SOC_SPDIF @@ -158,6 +159,9 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_MAX98088 + tristate + config SND_SOC_PCM3008 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0c5f329240c..f67a2d6f7a4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-l3-objs := l3.o +snd-soc-max98088-objs := max98088.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-spdif-objs := spdif_transciever.o snd-soc-ssm2602-objs := ssm2602.o @@ -49,7 +50,7 @@ snd-soc-wm8940-objs := wm8940.o snd-soc-wm8955-objs := wm8955.o snd-soc-wm8960-objs := wm8960.o snd-soc-wm8961-objs := wm8961.o -snd-soc-wm8962-objs := wm8962.o wm8962-tables.o +snd-soc-wm8962-objs := wm8962.o snd-soc-wm8971-objs := wm8971.o snd-soc-wm8974-objs := wm8974.o snd-soc-wm8978-objs := wm8978.o @@ -89,6 +90,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o +obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 2c824a58875..3c087936aa5 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -79,12 +79,6 @@ static int ac97_soc_probe(struct snd_soc_codec *codec) struct snd_ac97_template ac97_template; int ret; - ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); - if (ret < 0) { - printk(KERN_ERR "ASoC: failed to init generic ac97 glue\n"); - return ret; - } - /* add codec as bus device for standard ac97 */ ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus); if (ret < 0) @@ -168,3 +162,4 @@ module_exit(ac97_exit); MODULE_DESCRIPTION("Soc Generic AC97 driver"); MODULE_AUTHOR("Liam Girdwood"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ac97-codec"); diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index d36bd1c333b..410ccd5d41c 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -133,7 +133,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, return 0; } -struct snd_soc_dai_driver ad1980_dai = { +static struct snd_soc_dai_driver ad1980_dai = { .name = "ad1980-hifi", .ac97_control = 1, .playback = { diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index c53955fe17b..de799cd1ba7 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/ak4642.c b/sound/soc/codecs/ak4642.c index a95ff4f9f42..90c90b7f4a2 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -72,6 +72,12 @@ #define AK4642_CACHEREGNUM 0x25 +/* PW_MGMT1*/ +#define PMVCM (1 << 6) /* VCOM Power Management */ +#define PMMIN (1 << 5) /* MIN Input Power Management */ +#define PMDAC (1 << 2) /* DAC Power Management */ +#define PMADL (1 << 0) /* MIC Amp Lch and ADC Lch Power Management */ + /* PW_MGMT2 */ #define HPMTN (1 << 6) #define PMHPL (1 << 5) @@ -83,6 +89,23 @@ #define PMHP_MASK (PMHPL | PMHPR) #define PMHP PMHP_MASK +/* PW_MGMT3 */ +#define PMADR (1 << 0) /* MIC L / ADC R Power Management */ + +/* SG_SL1 */ +#define MINS (1 << 6) /* Switch from MIN to Speaker */ +#define DACL (1 << 4) /* Switch from DAC to Stereo or Receiver */ +#define PMMP (1 << 2) /* MPWR pin Power Management */ +#define MGAIN0 (1 << 0) /* MIC amp gain*/ + +/* TIMER */ +#define ZTM(param) ((param & 0x3) << 4) /* ALC Zoro Crossing TimeOut */ +#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2)) + +/* ALC_CTL1 */ +#define ALC (1 << 5) /* ALC Enable */ +#define LMTH0 (1 << 0) /* ALC Limiter / Recovery Level */ + /* MD_CTL1 */ #define PLL3 (1 << 7) #define PLL2 (1 << 6) @@ -100,6 +123,11 @@ #define FS3 (1 << 5) #define FS_MASK (FS0 | FS1 | FS2 | FS3) +/* MD_CTL3 */ +#define BST1 (1 << 3) + +/* MD_CTL4 */ +#define DACH (1 << 0) /* * Playback Volume (table 39) @@ -216,11 +244,12 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream, * This operation came from example code of * "ASAHI KASEI AK4642" (japanese) manual p97. */ - ak4642_write(codec, 0x0f, 0x09); - ak4642_write(codec, 0x0e, 0x19); - ak4642_write(codec, 0x09, 0x91); - ak4642_write(codec, 0x0c, 0x91); - ak4642_write(codec, 0x00, 0x64); + snd_soc_update_bits(codec, MD_CTL4, DACH, DACH); + snd_soc_update_bits(codec, MD_CTL3, BST1, BST1); + ak4642_write(codec, L_IVC, 0x91); /* volume */ + ak4642_write(codec, R_IVC, 0x91); /* volume */ + snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMMIN | PMDAC, + PMVCM | PMMIN | PMDAC); snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP); snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN); } else { @@ -237,13 +266,12 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream, * This operation came from example code of * "ASAHI KASEI AK4642" (japanese) manual p94. */ - ak4642_write(codec, 0x02, 0x05); - ak4642_write(codec, 0x06, 0x3c); - ak4642_write(codec, 0x08, 0xe1); - ak4642_write(codec, 0x0b, 0x00); - ak4642_write(codec, 0x07, 0x21); - ak4642_write(codec, 0x00, 0x41); - ak4642_write(codec, 0x10, 0x01); + ak4642_write(codec, SG_SL1, PMMP | MGAIN0); + ak4642_write(codec, TIMER, ZTM(0x3) | WTM(0x3)); + ak4642_write(codec, ALC_CTL1, ALC | LMTH0); + snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMADL, + PMVCM | PMADL); + snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR); } return 0; @@ -259,14 +287,14 @@ static void ak4642_dai_shutdown(struct snd_pcm_substream *substream, /* stop headphone output */ snd_soc_update_bits(codec, PW_MGMT2, HPMTN, 0); snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, 0); - ak4642_write(codec, 0x00, 0x40); - ak4642_write(codec, 0x0e, 0x11); - ak4642_write(codec, 0x0f, 0x08); + snd_soc_update_bits(codec, PW_MGMT1, PMMIN | PMDAC, 0); + snd_soc_update_bits(codec, MD_CTL3, BST1, 0); + snd_soc_update_bits(codec, MD_CTL4, DACH, 0); } else { /* stop stereo input */ - ak4642_write(codec, 0x00, 0x40); - ak4642_write(codec, 0x10, 0x00); - ak4642_write(codec, 0x07, 0x01); + snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0); + snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0); + snd_soc_update_bits(codec, ALC_CTL1, ALC, 0); } } diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 5fd8e0d0dab..6d4bdc609ac 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -518,7 +518,7 @@ static struct snd_soc_dai_ops cs4270_dai_ops = { .digital_mute = cs4270_dai_mute, }; -struct snd_soc_dai_driver cs4270_dai = { +static struct snd_soc_dai_driver cs4270_dai = { .name = "cs4270-hifi", .playback = { .stream_name = "Playback", diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c new file mode 100644 index 00000000000..bc22ee93a75 --- /dev/null +++ b/sound/soc/codecs/max98088.c @@ -0,0 +1,2097 @@ +/* + * max98088.c -- MAX98088 ALSA SoC Audio driver + * + * Copyright 2010 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <linux/slab.h> +#include <asm/div64.h> +#include <sound/max98088.h> +#include "max98088.h" + +struct max98088_cdata { + unsigned int rate; + unsigned int fmt; + int eq_sel; +}; + +struct max98088_priv { + u8 reg_cache[M98088_REG_CNT]; + void *control_data; + struct max98088_pdata *pdata; + unsigned int sysclk; + struct max98088_cdata dai[2]; + int eq_textcnt; + const char **eq_texts; + struct soc_enum eq_enum; + u8 ina_state; + u8 inb_state; + unsigned int ex_mode; + unsigned int digmic; + unsigned int mic1pre; + unsigned int mic2pre; + unsigned int extmic_mode; +}; + +static const u8 max98088_reg[M98088_REG_CNT] = { + 0x00, /* 00 IRQ status */ + 0x00, /* 01 MIC status */ + 0x00, /* 02 jack status */ + 0x00, /* 03 battery voltage */ + 0x00, /* 04 */ + 0x00, /* 05 */ + 0x00, /* 06 */ + 0x00, /* 07 */ + 0x00, /* 08 */ + 0x00, /* 09 */ + 0x00, /* 0A */ + 0x00, /* 0B */ + 0x00, /* 0C */ + 0x00, /* 0D */ + 0x00, /* 0E */ + 0x00, /* 0F interrupt enable */ + + 0x00, /* 10 master clock */ + 0x00, /* 11 DAI1 clock mode */ + 0x00, /* 12 DAI1 clock control */ + 0x00, /* 13 DAI1 clock control */ + 0x00, /* 14 DAI1 format */ + 0x00, /* 15 DAI1 clock */ + 0x00, /* 16 DAI1 config */ + 0x00, /* 17 DAI1 TDM */ + 0x00, /* 18 DAI1 filters */ + 0x00, /* 19 DAI2 clock mode */ + 0x00, /* 1A DAI2 clock control */ + 0x00, /* 1B DAI2 clock control */ + 0x00, /* 1C DAI2 format */ + 0x00, /* 1D DAI2 clock */ + 0x00, /* 1E DAI2 config */ + 0x00, /* 1F DAI2 TDM */ + + 0x00, /* 20 DAI2 filters */ + 0x00, /* 21 data config */ + 0x00, /* 22 DAC mixer */ + 0x00, /* 23 left ADC mixer */ + 0x00, /* 24 right ADC mixer */ + 0x00, /* 25 left HP mixer */ + 0x00, /* 26 right HP mixer */ + 0x00, /* 27 HP control */ + 0x00, /* 28 left REC mixer */ + 0x00, /* 29 right REC mixer */ + 0x00, /* 2A REC control */ + 0x00, /* 2B left SPK mixer */ + 0x00, /* 2C right SPK mixer */ + 0x00, /* 2D SPK control */ + 0x00, /* 2E sidetone */ + 0x00, /* 2F DAI1 playback level */ + + 0x00, /* 30 DAI1 playback level */ + 0x00, /* 31 DAI2 playback level */ + 0x00, /* 32 DAI2 playbakc level */ + 0x00, /* 33 left ADC level */ + 0x00, /* 34 right ADC level */ + 0x00, /* 35 MIC1 level */ + 0x00, /* 36 MIC2 level */ + 0x00, /* 37 INA level */ + 0x00, /* 38 INB level */ + 0x00, /* 39 left HP volume */ + 0x00, /* 3A right HP volume */ + 0x00, /* 3B left REC volume */ + 0x00, /* 3C right REC volume */ + 0x00, /* 3D left SPK volume */ + 0x00, /* 3E right SPK volume */ + 0x00, /* 3F MIC config */ + + 0x00, /* 40 MIC threshold */ + 0x00, /* 41 excursion limiter filter */ + 0x00, /* 42 excursion limiter threshold */ + 0x00, /* 43 ALC */ + 0x00, /* 44 power limiter threshold */ + 0x00, /* 45 power limiter config */ + 0x00, /* 46 distortion limiter config */ + 0x00, /* 47 audio input */ + 0x00, /* 48 microphone */ + 0x00, /* 49 level control */ + 0x00, /* 4A bypass switches */ + 0x00, /* 4B jack detect */ + 0x00, /* 4C input enable */ + 0x00, /* 4D output enable */ + 0xF0, /* 4E bias control */ + 0x00, /* 4F DAC power */ + + 0x0F, /* 50 DAC power */ + 0x00, /* 51 system */ + 0x00, /* 52 DAI1 EQ1 */ + 0x00, /* 53 DAI1 EQ1 */ + 0x00, /* 54 DAI1 EQ1 */ + 0x00, /* 55 DAI1 EQ1 */ + 0x00, /* 56 DAI1 EQ1 */ + 0x00, /* 57 DAI1 EQ1 */ + 0x00, /* 58 DAI1 EQ1 */ + 0x00, /* 59 DAI1 EQ1 */ + 0x00, /* 5A DAI1 EQ1 */ + 0x00, /* 5B DAI1 EQ1 */ + 0x00, /* 5C DAI1 EQ2 */ + 0x00, /* 5D DAI1 EQ2 */ + 0x00, /* 5E DAI1 EQ2 */ + 0x00, /* 5F DAI1 EQ2 */ + + 0x00, /* 60 DAI1 EQ2 */ + 0x00, /* 61 DAI1 EQ2 */ + 0x00, /* 62 DAI1 EQ2 */ + 0x00, /* 63 DAI1 EQ2 */ + 0x00, /* 64 DAI1 EQ2 */ + 0x00, /* 65 DAI1 EQ2 */ + 0x00, /* 66 DAI1 EQ3 */ + 0x00, /* 67 DAI1 EQ3 */ + 0x00, /* 68 DAI1 EQ3 */ + 0x00, /* 69 DAI1 EQ3 */ + 0x00, /* 6A DAI1 EQ3 */ + 0x00, /* 6B DAI1 EQ3 */ + 0x00, /* 6C DAI1 EQ3 */ + 0x00, /* 6D DAI1 EQ3 */ + 0x00, /* 6E DAI1 EQ3 */ + 0x00, /* 6F DAI1 EQ3 */ + + 0x00, /* 70 DAI1 EQ4 */ + 0x00, /* 71 DAI1 EQ4 */ + 0x00, /* 72 DAI1 EQ4 */ + 0x00, /* 73 DAI1 EQ4 */ + 0x00, /* 74 DAI1 EQ4 */ + 0x00, /* 75 DAI1 EQ4 */ + 0x00, /* 76 DAI1 EQ4 */ + 0x00, /* 77 DAI1 EQ4 */ + 0x00, /* 78 DAI1 EQ4 */ + 0x00, /* 79 DAI1 EQ4 */ + 0x00, /* 7A DAI1 EQ5 */ + 0x00, /* 7B DAI1 EQ5 */ + 0x00, /* 7C DAI1 EQ5 */ + 0x00, /* 7D DAI1 EQ5 */ + 0x00, /* 7E DAI1 EQ5 */ + 0x00, /* 7F DAI1 EQ5 */ + + 0x00, /* 80 DAI1 EQ5 */ + 0x00, /* 81 DAI1 EQ5 */ + 0x00, /* 82 DAI1 EQ5 */ + 0x00, /* 83 DAI1 EQ5 */ + 0x00, /* 84 DAI2 EQ1 */ + 0x00, /* 85 DAI2 EQ1 */ + 0x00, /* 86 DAI2 EQ1 */ + 0x00, /* 87 DAI2 EQ1 */ + 0x00, /* 88 DAI2 EQ1 */ + 0x00, /* 89 DAI2 EQ1 */ + 0x00, /* 8A DAI2 EQ1 */ + 0x00, /* 8B DAI2 EQ1 */ + 0x00, /* 8C DAI2 EQ1 */ + 0x00, /* 8D DAI2 EQ1 */ + 0x00, /* 8E DAI2 EQ2 */ + 0x00, /* 8F DAI2 EQ2 */ + + 0x00, /* 90 DAI2 EQ2 */ + 0x00, /* 91 DAI2 EQ2 */ + 0x00, /* 92 DAI2 EQ2 */ + 0x00, /* 93 DAI2 EQ2 */ + 0x00, /* 94 DAI2 EQ2 */ + 0x00, /* 95 DAI2 EQ2 */ + 0x00, /* 96 DAI2 EQ2 */ + 0x00, /* 97 DAI2 EQ2 */ + 0x00, /* 98 DAI2 EQ3 */ + 0x00, /* 99 DAI2 EQ3 */ + 0x00, /* 9A DAI2 EQ3 */ + 0x00, /* 9B DAI2 EQ3 */ + 0x00, /* 9C DAI2 EQ3 */ + 0x00, /* 9D DAI2 EQ3 */ + 0x00, /* 9E DAI2 EQ3 */ + 0x00, /* 9F DAI2 EQ3 */ + + 0x00, /* A0 DAI2 EQ3 */ + 0x00, /* A1 DAI2 EQ3 */ + 0x00, /* A2 DAI2 EQ4 */ + 0x00, /* A3 DAI2 EQ4 */ + 0x00, /* A4 DAI2 EQ4 */ + 0x00, /* A5 DAI2 EQ4 */ + 0x00, /* A6 DAI2 EQ4 */ + 0x00, /* A7 DAI2 EQ4 */ + 0x00, /* A8 DAI2 EQ4 */ + 0x00, /* A9 DAI2 EQ4 */ + 0x00, /* AA DAI2 EQ4 */ + 0x00, /* AB DAI2 EQ4 */ + 0x00, /* AC DAI2 EQ5 */ + 0x00, /* AD DAI2 EQ5 */ + 0x00, /* AE DAI2 EQ5 */ + 0x00, /* AF DAI2 EQ5 */ + + 0x00, /* B0 DAI2 EQ5 */ + 0x00, /* B1 DAI2 EQ5 */ + 0x00, /* B2 DAI2 EQ5 */ + 0x00, /* B3 DAI2 EQ5 */ + 0x00, /* B4 DAI2 EQ5 */ + 0x00, /* B5 DAI2 EQ5 */ + 0x00, /* B6 DAI1 biquad */ + 0x00, /* B7 DAI1 biquad */ + 0x00, /* B8 DAI1 biquad */ + 0x00, /* B9 DAI1 biquad */ + 0x00, /* BA DAI1 biquad */ + 0x00, /* BB DAI1 biquad */ + 0x00, /* BC DAI1 biquad */ + 0x00, /* BD DAI1 biquad */ + 0x00, /* BE DAI1 biquad */ + 0x00, /* BF DAI1 biquad */ + + 0x00, /* C0 DAI2 biquad */ + 0x00, /* C1 DAI2 biquad */ + 0x00, /* C2 DAI2 biquad */ + 0x00, /* C3 DAI2 biquad */ + 0x00, /* C4 DAI2 biquad */ + 0x00, /* C5 DAI2 biquad */ + 0x00, /* C6 DAI2 biquad */ + 0x00, /* C7 DAI2 biquad */ + 0x00, /* C8 DAI2 biquad */ + 0x00, /* C9 DAI2 biquad */ + 0x00, /* CA */ + 0x00, /* CB */ + 0x00, /* CC */ + 0x00, /* CD */ + 0x00, /* CE */ + 0x00, /* CF */ + + 0x00, /* D0 */ + 0x00, /* D1 */ + 0x00, /* D2 */ + 0x00, /* D3 */ + 0x00, /* D4 */ + 0x00, /* D5 */ + 0x00, /* D6 */ + 0x00, /* D7 */ + 0x00, /* D8 */ + 0x00, /* D9 */ + 0x00, /* DA */ + 0x70, /* DB */ + 0x00, /* DC */ + 0x00, /* DD */ + 0x00, /* DE */ + 0x00, /* DF */ + + 0x00, /* E0 */ + 0x00, /* E1 */ + 0x00, /* E2 */ + 0x00, /* E3 */ + 0x00, /* E4 */ + 0x00, /* E5 */ + 0x00, /* E6 */ + 0x00, /* E7 */ + 0x00, /* E8 */ + 0x00, /* E9 */ + 0x00, /* EA */ + 0x00, /* EB */ + 0x00, /* EC */ + 0x00, /* ED */ + 0x00, /* EE */ + 0x00, /* EF */ + + 0x00, /* F0 */ + 0x00, /* F1 */ + 0x00, /* F2 */ + 0x00, /* F3 */ + 0x00, /* F4 */ + 0x00, /* F5 */ + 0x00, /* F6 */ + 0x00, /* F7 */ + 0x00, /* F8 */ + 0x00, /* F9 */ + 0x00, /* FA */ + 0x00, /* FB */ + 0x00, /* FC */ + 0x00, /* FD */ + 0x00, /* FE */ + 0x00, /* FF */ +}; + +static struct { + int readable; + int writable; + int vol; +} max98088_access[M98088_REG_CNT] = { + { 0xFF, 0xFF, 1 }, /* 00 IRQ status */ + { 0xFF, 0x00, 1 }, /* 01 MIC status */ + { 0xFF, 0x00, 1 }, /* 02 jack status */ + { 0x1F, 0x1F, 1 }, /* 03 battery voltage */ + { 0xFF, 0xFF, 0 }, /* 04 */ + { 0xFF, 0xFF, 0 }, /* 05 */ + { 0xFF, 0xFF, 0 }, /* 06 */ + { 0xFF, 0xFF, 0 }, /* 07 */ + { 0xFF, 0xFF, 0 }, /* 08 */ + { 0xFF, 0xFF, 0 }, /* 09 */ + { 0xFF, 0xFF, 0 }, /* 0A */ + { 0xFF, 0xFF, 0 }, /* 0B */ + { 0xFF, 0xFF, 0 }, /* 0C */ + { 0xFF, 0xFF, 0 }, /* 0D */ + { 0xFF, 0xFF, 0 }, /* 0E */ + { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */ + + { 0xFF, 0xFF, 0 }, /* 10 master clock */ + { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */ + { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */ + { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */ + { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */ + { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */ + { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */ + { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */ + { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */ + { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */ + { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */ + { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */ + { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */ + { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */ + { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */ + { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */ + + { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */ + { 0xFF, 0xFF, 0 }, /* 21 data config */ + { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */ + { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */ + { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */ + { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */ + { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */ + { 0xFF, 0xFF, 0 }, /* 27 HP control */ + { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */ + { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */ + { 0xFF, 0xFF, 0 }, /* 2A REC control */ + { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */ + { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */ + { 0xFF, 0xFF, 0 }, /* 2D SPK control */ + { 0xFF, 0xFF, 0 }, /* 2E sidetone */ + { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */ + + { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */ + { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */ + { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */ + { 0xFF, 0xFF, 0 }, /* 33 left ADC level */ + { 0xFF, 0xFF, 0 }, /* 34 right ADC level */ + { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */ + { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */ + { 0xFF, 0xFF, 0 }, /* 37 INA level */ + { 0xFF, 0xFF, 0 }, /* 38 INB level */ + { 0xFF, 0xFF, 0 }, /* 39 left HP volume */ + { 0xFF, 0xFF, 0 }, /* 3A right HP volume */ + { 0xFF, 0xFF, 0 }, /* 3B left REC volume */ + { 0xFF, 0xFF, 0 }, /* 3C right REC volume */ + { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */ + { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */ + { 0xFF, 0xFF, 0 }, /* 3F MIC config */ + + { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */ + { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */ + { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */ + { 0xFF, 0xFF, 0 }, /* 43 ALC */ + { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */ + { 0xFF, 0xFF, 0 }, /* 45 power limiter config */ + { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */ + { 0xFF, 0xFF, 0 }, /* 47 audio input */ + { 0xFF, 0xFF, 0 }, /* 48 microphone */ + { 0xFF, 0xFF, 0 }, /* 49 level control */ + { 0xFF, 0xFF, 0 }, /* 4A bypass switches */ + { 0xFF, 0xFF, 0 }, /* 4B jack detect */ + { 0xFF, 0xFF, 0 }, /* 4C input enable */ + { 0xFF, 0xFF, 0 }, /* 4D output enable */ + { 0xFF, 0xFF, 0 }, /* 4E bias control */ + { 0xFF, 0xFF, 0 }, /* 4F DAC power */ + + { 0xFF, 0xFF, 0 }, /* 50 DAC power */ + { 0xFF, 0xFF, 0 }, /* 51 system */ + { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */ + + { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */ + + { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */ + { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */ + + { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */ + { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */ + { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */ + + { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */ + { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */ + + { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */ + { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */ + { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */ + + { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */ + { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */ + { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */ + + { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */ + { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */ + { 0x00, 0x00, 0 }, /* CA */ + { 0x00, 0x00, 0 }, /* CB */ + { 0x00, 0x00, 0 }, /* CC */ + { 0x00, 0x00, 0 }, /* CD */ + { 0x00, 0x00, 0 }, /* CE */ + { 0x00, 0x00, 0 }, /* CF */ + + { 0x00, 0x00, 0 }, /* D0 */ + { 0x00, 0x00, 0 }, /* D1 */ + { 0x00, 0x00, 0 }, /* D2 */ + { 0x00, 0x00, 0 }, /* D3 */ + { 0x00, 0x00, 0 }, /* D4 */ + { 0x00, 0x00, 0 }, /* D5 */ + { 0x00, 0x00, 0 }, /* D6 */ + { 0x00, 0x00, 0 }, /* D7 */ + { 0x00, 0x00, 0 }, /* D8 */ + { 0x00, 0x00, 0 }, /* D9 */ + { 0x00, 0x00, 0 }, /* DA */ + { 0x00, 0x00, 0 }, /* DB */ + { 0x00, 0x00, 0 }, /* DC */ + { 0x00, 0x00, 0 }, /* DD */ + { 0x00, 0x00, 0 }, /* DE */ + { 0x00, 0x00, 0 }, /* DF */ + + { 0x00, 0x00, 0 }, /* E0 */ + { 0x00, 0x00, 0 }, /* E1 */ + { 0x00, 0x00, 0 }, /* E2 */ + { 0x00, 0x00, 0 }, /* E3 */ + { 0x00, 0x00, 0 }, /* E4 */ + { 0x00, 0x00, 0 }, /* E5 */ + { 0x00, 0x00, 0 }, /* E6 */ + { 0x00, 0x00, 0 }, /* E7 */ + { 0x00, 0x00, 0 }, /* E8 */ + { 0x00, 0x00, 0 }, /* E9 */ + { 0x00, 0x00, 0 }, /* EA */ + { 0x00, 0x00, 0 }, /* EB */ + { 0x00, 0x00, 0 }, /* EC */ + { 0x00, 0x00, 0 }, /* ED */ + { 0x00, 0x00, 0 }, /* EE */ + { 0x00, 0x00, 0 }, /* EF */ + + { 0x00, 0x00, 0 }, /* F0 */ + { 0x00, 0x00, 0 }, /* F1 */ + { 0x00, 0x00, 0 }, /* F2 */ + { 0x00, 0x00, 0 }, /* F3 */ + { 0x00, 0x00, 0 }, /* F4 */ + { 0x00, 0x00, 0 }, /* F5 */ + { 0x00, 0x00, 0 }, /* F6 */ + { 0x00, 0x00, 0 }, /* F7 */ + { 0x00, 0x00, 0 }, /* F8 */ + { 0x00, 0x00, 0 }, /* F9 */ + { 0x00, 0x00, 0 }, /* FA */ + { 0x00, 0x00, 0 }, /* FB */ + { 0x00, 0x00, 0 }, /* FC */ + { 0x00, 0x00, 0 }, /* FD */ + { 0x00, 0x00, 0 }, /* FE */ + { 0xFF, 0x00, 1 }, /* FF */ +}; + +static int max98088_volatile_register(unsigned int reg) +{ + return max98088_access[reg].vol; +} + + +/* + * Load equalizer DSP coefficient configurations registers + */ +static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai, + unsigned int band, u16 *coefs) +{ + unsigned int eq_reg; + unsigned int i; + + BUG_ON(band > 4); + BUG_ON(dai > 1); + + /* Load the base register address */ + eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; + + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98088_COEFS_PER_BAND << 1); + + /* Step through the registers and coefs */ + for (i = 0; i < M98088_COEFS_PER_BAND; i++) { + snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i])); + snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i])); + } +} + +/* + * Excursion limiter modes + */ +static const char *max98088_exmode_texts[] = { + "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz", + "400-600Hz", "400-800Hz", +}; + +static const unsigned int max98088_exmode_values[] = { + 0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32 +}; + +static const struct soc_enum max98088_exmode_enum = + SOC_VALUE_ENUM_SINGLE(M98088_REG_41_SPKDHP, 0, 127, + ARRAY_SIZE(max98088_exmode_texts), + max98088_exmode_texts, + max98088_exmode_values); +static const struct snd_kcontrol_new max98088_exmode_controls = + SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum); + +static const char *max98088_ex_thresh[] = { /* volts PP */ + "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"}; +static const struct soc_enum max98088_ex_thresh_enum[] = { + SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8, + max98088_ex_thresh), +}; + +static const char *max98088_fltr_mode[] = {"Voice", "Music" }; +static const struct soc_enum max98088_filter_mode_enum[] = { + SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode), +}; + +static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" }; + +static const struct soc_enum max98088_extmic_enum = + SOC_ENUM_SINGLE(M98088_REG_48_CFG_MIC, 0, 3, max98088_extmic_text); + +static const struct snd_kcontrol_new max98088_extmic_mux = + SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum); + +static const char *max98088_dai1_fltr[] = { + "Off", "fc=258/fs=16k", "fc=500/fs=16k", + "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"}; +static const struct soc_enum max98088_dai1_dac_filter_enum[] = { + SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr), +}; +static const struct soc_enum max98088_dai1_adc_filter_enum[] = { + SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr), +}; + +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98088->mic1pre = sel; + snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK, + (1+sel)<<M98088_MICPRE_SHIFT); + + return 0; +} + +static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98088->mic1pre; + return 0; +} + +static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98088->mic2pre = sel; + snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK, + (1+sel)<<M98088_MICPRE_SHIFT); + + return 0; +} + +static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98088->mic2pre; + return 0; +} + +static const unsigned int max98088_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const struct snd_kcontrol_new max98088_snd_controls[] = { + + SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L, + M98088_REG_3A_LVL_HP_R, 0, 31, 0), + SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L, + M98088_REG_3E_LVL_SPK_R, 0, 31, 0), + SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L, + M98088_REG_3C_LVL_REC_R, 0, 31, 0), + + SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L, + M98088_REG_3A_LVL_HP_R, 7, 1, 1), + SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L, + M98088_REG_3E_LVL_SPK_R, 7, 1, 1), + SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L, + M98088_REG_3C_LVL_REC_R, 7, 1, 1), + + SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1), + SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98088_REG_35_LVL_MIC1, 5, 2, 0, + max98088_mic1pre_get, max98088_mic1pre_set, + max98088_micboost_tlv), + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98088_REG_36_LVL_MIC2, 5, 2, 0, + max98088_mic2pre_get, max98088_mic2pre_set, + max98088_micboost_tlv), + + SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1), + SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1), + + SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0), + SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0), + + SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0), + SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0), + + SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0), + SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0), + + SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum), + + SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum), + SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum), + SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum), + SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS, + 0, 1, 0), + + SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0), + SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0), + SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0), + SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0), + + SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG, + 4, 15, 0), + SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0), + SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0), + SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0), + + SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0), + SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0), +}; + +/* Left speaker mixer switch */ +static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0), +}; + +/* Left earpiece/receiver mixer switch */ +static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0), +}; + +/* Right earpiece/receiver mixer switch */ +static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0), +}; + +static int max98088_mic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->reg == M98088_REG_35_LVL_MIC1) { + snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, + (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT); + } else { + snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, + (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * The line inputs are 2-channel stereo inputs with the left + * and right channels sharing a common PGA power control signal. + */ +static int max98088_line_pga(struct snd_soc_dapm_widget *w, + int event, int line, u8 channel) +{ + struct snd_soc_codec *codec = w->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + u8 *state; + + BUG_ON(!((channel == 1) || (channel == 2))); + + switch (line) { + case LINE_INA: + state = &max98088->ina_state; + break; + case LINE_INB: + state = &max98088->inb_state; + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + *state |= channel; + snd_soc_update_bits(codec, w->reg, + (1 << w->shift), (1 << w->shift)); + break; + case SND_SOC_DAPM_POST_PMD: + *state &= ~channel; + if (*state == 0) { + snd_soc_update_bits(codec, w->reg, + (1 << w->shift), 0); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INA, 1); +} + +static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INA, 2); +} + +static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INB, 1); +} + +static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INB, 2); +} + +static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0), + + SND_SOC_DAPM_DAC("DACL1", "HiFi Playback", + M98088_REG_4D_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACR1", "HiFi Playback", + M98088_REG_4D_PWR_EN_OUT, 0, 0), + SND_SOC_DAPM_DAC("DACL2", "Aux Playback", + M98088_REG_4D_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACR2", "Aux Playback", + M98088_REG_4D_PWR_EN_OUT, 0, 0), + + SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT, + 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT, + 4, 0, NULL, 0), + + SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT, + 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT, + 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0, + &max98088_extmic_mux), + + SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_hp_mixer_controls[0], + ARRAY_SIZE(max98088_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_hp_mixer_controls[0], + ARRAY_SIZE(max98088_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98088_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98088_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_rec_mixer_controls[0], + ARRAY_SIZE(max98088_left_rec_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_rec_mixer_controls[0], + ARRAY_SIZE(max98088_right_rec_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_ADC_mixer_controls[0], + ARRAY_SIZE(max98088_left_ADC_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_ADC_mixer_controls[0], + ARRAY_SIZE(max98088_right_ADC_mixer_controls)), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1, + 5, 0, NULL, 0, max98088_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2, + 5, 0, NULL, 0, max98088_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN, + 7, 0, NULL, 0, max98088_pga_ina1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN, + 7, 0, NULL, 0, max98088_pga_ina2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN, + 6, 0, NULL, 0, max98088_pga_inb1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN, + 6, 0, NULL, 0, max98088_pga_inb2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0), + + SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0, + &max98088_exmode_controls), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RECL"), + SND_SOC_DAPM_OUTPUT("RECR"), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("INA1"), + SND_SOC_DAPM_INPUT("INA2"), + SND_SOC_DAPM_INPUT("INB1"), + SND_SOC_DAPM_INPUT("INB2"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Left headphone output mixer */ + {"Left HP Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left HP Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left HP Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left HP Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left HP Mixer", "INA1 Switch", "INA1 Input"}, + {"Left HP Mixer", "INA2 Switch", "INA2 Input"}, + {"Left HP Mixer", "INB1 Switch", "INB1 Input"}, + {"Left HP Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right headphone output mixer */ + {"Right HP Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right HP Mixer", "Left DAC2 Switch", "DACL2" }, + {"Right HP Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right HP Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right HP Mixer", "INA1 Switch", "INA1 Input"}, + {"Right HP Mixer", "INA2 Switch", "INA2 Input"}, + {"Right HP Mixer", "INB1 Switch", "INB1 Input"}, + {"Right HP Mixer", "INB2 Switch", "INB2 Input"}, + + /* Left speaker output mixer */ + {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left SPK Mixer", "INA1 Switch", "INA1 Input"}, + {"Left SPK Mixer", "INA2 Switch", "INA2 Input"}, + {"Left SPK Mixer", "INB1 Switch", "INB1 Input"}, + {"Left SPK Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right speaker output mixer */ + {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"}, + {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right SPK Mixer", "INA1 Switch", "INA1 Input"}, + {"Right SPK Mixer", "INA2 Switch", "INA2 Input"}, + {"Right SPK Mixer", "INB1 Switch", "INB1 Input"}, + {"Right SPK Mixer", "INB2 Switch", "INB2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Left REC Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left REC Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left REC Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left REC Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left REC Mixer", "INA1 Switch", "INA1 Input"}, + {"Left REC Mixer", "INA2 Switch", "INA2 Input"}, + {"Left REC Mixer", "INB1 Switch", "INB1 Input"}, + {"Left REC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Right REC Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right REC Mixer", "Left DAC2 Switch", "DACL2"}, + {"Right REC Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right REC Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right REC Mixer", "INA1 Switch", "INA1 Input"}, + {"Right REC Mixer", "INA2 Switch", "INA2 Input"}, + {"Right REC Mixer", "INB1 Switch", "INB1 Input"}, + {"Right REC Mixer", "INB2 Switch", "INB2 Input"}, + + {"HP Left Out", NULL, "Left HP Mixer"}, + {"HP Right Out", NULL, "Right HP Mixer"}, + {"SPK Left Out", NULL, "Left SPK Mixer"}, + {"SPK Right Out", NULL, "Right SPK Mixer"}, + {"REC Left Out", NULL, "Left REC Mixer"}, + {"REC Right Out", NULL, "Right REC Mixer"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RECL", NULL, "REC Left Out"}, + {"RECR", NULL, "REC Right Out"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left ADC Mixer", "INA1 Switch", "INA1 Input"}, + {"Left ADC Mixer", "INA2 Switch", "INA2 Input"}, + {"Left ADC Mixer", "INB1 Switch", "INB1 Input"}, + {"Left ADC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right ADC Mixer", "INA1 Switch", "INA1 Input"}, + {"Right ADC Mixer", "INA2 Switch", "INA2 Input"}, + {"Right ADC Mixer", "INB1 Switch", "INB1 Input"}, + {"Right ADC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + {"INA1 Input", NULL, "INA1"}, + {"INA2 Input", NULL, "INA2"}, + {"INB1 Input", NULL, "INB1"}, + {"INB2 Input", NULL, "INB2"}, + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, +}; + +static int max98088_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, max98088_dapm_widgets, + ARRAY_SIZE(max98088_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_add_controls(codec, max98088_snd_controls, + ARRAY_SIZE(max98088_snd_controls)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +/* codec mclk clock divider coefficients */ +static const struct { + u32 rate; + u8 sr; +} rate_table[] = { + {8000, 0x10}, + {11025, 0x20}, + {16000, 0x30}, + {22050, 0x40}, + {24000, 0x50}, + {32000, 0x60}, + {44100, 0x70}, + {48000, 0x80}, + {88200, 0x90}, + {96000, 0xA0}, +}; + +static inline int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + return 0; + } + } + *value = rate_table[0].sr; + return -EINVAL; +} + +static int max98088_dai1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98088->dai[0]; + + rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_WS, 0); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_WS, M98088_DAI_WS); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0); + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE, + M98088_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT) + & M98088_DAI_MAS) { + if (max98088->sysclk == 0) { + dev_err(codec->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98088->sysclk); + snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS, + M98088_DAI_DHF, 0); + else + snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS, + M98088_DAI_DHF, M98088_DAI_DHF); + + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, + M98088_SHDNRUN); + + return 0; +} + +static int max98088_dai2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98088->dai[1]; + + rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_WS, 0); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_WS, M98088_DAI_WS); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0); + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE, + M98088_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT) + & M98088_DAI_MAS) { + if (max98088->sysclk == 0) { + dev_err(codec->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98088->sysclk); + snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS, + M98088_DAI_DHF, 0); + else + snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS, + M98088_DAI_DHF, M98088_DAI_DHF); + + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, + M98088_SHDNRUN); + + return 0; +} + +static int max98088_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + + /* Requested clock frequency is already setup */ + if (freq == max98088->sysclk) + return 0; + + max98088->sysclk = freq; /* remember current sysclk */ + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 30MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10); + } else if ((freq >= 20000000) && (freq < 30000000)) { + snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20); + } else { + dev_err(codec->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + if (snd_soc_read(codec, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) { + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, + M98088_SHDNRUN, 0); + snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, + M98088_SHDNRUN, M98088_SHDNRUN); + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + max98088->sysclk = freq; + return 0; +} + +static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_cdata *cdata; + u8 reg15val; + u8 reg14val = 0; + + cdata = &max98088->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI, + 0x80); + snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + reg14val |= M98088_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg14val |= M98088_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + reg14val |= M98088_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + reg14val |= M98088_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + reg14val |= M98088_DAI_BCI|M98088_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI | + M98088_DAI_WCI, reg14val); + + reg15val = M98088_DAI_BSEL64; + if (max98088->digmic) + reg15val |= M98088_DAI_OSR64; + snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val); + } + + return 0; +} + +static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_cdata *cdata; + u8 reg1Cval = 0; + + cdata = &max98088->dai[1]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI, + 0x80); + snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + reg1Cval |= M98088_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg1Cval |= M98088_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + reg1Cval |= M98088_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + reg1Cval |= M98088_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI | + M98088_DAI_WCI, reg1Cval); + + snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK, + M98088_DAI_BSEL64); + } + + return 0; +} + +static void max98088_sync_cache(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + int i; + + if (!codec->cache_sync) + return; + + codec->cache_only = 0; + + /* write back cached values if they're writeable and + * different from the hardware default. + */ + for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) { + if (!max98088_access[i].writable) + continue; + + if (max98088->reg_cache[i] == max98088_reg[i]) + continue; + + snd_soc_write(codec, i, max98088->reg_cache[i]); + } + + codec->cache_sync = 0; +} + +static int max98088_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_OFF) + max98088_sync_cache(codec); + + snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, M98088_MBEN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, 0); + codec->cache_sync = 1; + break; + } + codec->bias_level = level; + return 0; +} + +#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max98088_dai1_ops = { + .set_sysclk = max98088_dai_set_sysclk, + .set_fmt = max98088_dai1_set_fmt, + .hw_params = max98088_dai1_hw_params, +}; + +static struct snd_soc_dai_ops max98088_dai2_ops = { + .set_sysclk = max98088_dai_set_sysclk, + .set_fmt = max98088_dai2_set_fmt, + .hw_params = max98088_dai2_hw_params, +}; + +static struct snd_soc_dai_driver max98088_dai[] = { +{ + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .ops = &max98088_dai1_ops, +}, +{ + .name = "Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .ops = &max98088_dai2_ops, +} +}; + +static int max98088_get_channel(const char *name) +{ + if (strcmp(name, "EQ1 Mode") == 0) + return 0; + if (strcmp(name, "EQ2 Mode") == 0) + return 1; + return -EINVAL; +} + +static void max98088_setup_eq1(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *coef_set; + int best, best_val, save, i, sel, fs; + struct max98088_cdata *cdata; + + cdata = &max98088->dai[0]; + + if (!pdata || !max98088->eq_textcnt) + return; + + /* Find the selected configuration with nearest sample rate */ + fs = cdata->rate; + sel = cdata->eq_sel; + + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->eq_cfgcnt; i++) { + if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 && + abs(pdata->eq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->eq_cfg[i].rate - fs); + } + } + + dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->eq_cfg[best].name, + pdata->eq_cfg[best].rate, fs); + + /* Disable EQ while configuring, and save current on/off state */ + save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL); + snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0); + + coef_set = &pdata->eq_cfg[sel]; + + m98088_eq_band(codec, 0, 0, coef_set->band1); + m98088_eq_band(codec, 0, 1, coef_set->band2); + m98088_eq_band(codec, 0, 2, coef_set->band3); + m98088_eq_band(codec, 0, 3, coef_set->band4); + m98088_eq_band(codec, 0, 4, coef_set->band5); + + /* Restore the original on/off state */ + snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save); +} + +static void max98088_setup_eq2(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *coef_set; + int best, best_val, save, i, sel, fs; + struct max98088_cdata *cdata; + + cdata = &max98088->dai[1]; + + if (!pdata || !max98088->eq_textcnt) + return; + + /* Find the selected configuration with nearest sample rate */ + fs = cdata->rate; + + sel = cdata->eq_sel; + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->eq_cfgcnt; i++) { + if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 && + abs(pdata->eq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->eq_cfg[i].rate - fs); + } + } + + dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->eq_cfg[best].name, + pdata->eq_cfg[best].rate, fs); + + /* Disable EQ while configuring, and save current on/off state */ + save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL); + snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0); + + coef_set = &pdata->eq_cfg[sel]; + + m98088_eq_band(codec, 1, 0, coef_set->band1); + m98088_eq_band(codec, 1, 1, coef_set->band2); + m98088_eq_band(codec, 1, 2, coef_set->band3); + m98088_eq_band(codec, 1, 3, coef_set->band4); + m98088_eq_band(codec, 1, 4, coef_set->band5); + + /* Restore the original on/off state */ + snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, + save); +} + +static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_pdata *pdata = max98088->pdata; + int channel = max98088_get_channel(kcontrol->id.name); + struct max98088_cdata *cdata; + int sel = ucontrol->value.integer.value[0]; + + cdata = &max98088->dai[channel]; + + if (sel >= pdata->eq_cfgcnt) + return -EINVAL; + + cdata->eq_sel = sel; + + switch (channel) { + case 0: + max98088_setup_eq1(codec); + break; + case 1: + max98088_setup_eq2(codec); + break; + } + + return 0; +} + +static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + int channel = max98088_get_channel(kcontrol->id.name); + struct max98088_cdata *cdata; + + cdata = &max98088->dai[channel]; + ucontrol->value.enumerated.item[0] = cdata->eq_sel; + return 0; +} + +static void max98088_handle_eq_pdata(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *cfg; + unsigned int cfgcnt; + int i, j; + const char **t; + int ret; + + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT("EQ1 Mode", + max98088->eq_enum, + max98088_get_eq_enum, + max98088_put_eq_enum), + SOC_ENUM_EXT("EQ2 Mode", + max98088->eq_enum, + max98088_get_eq_enum, + max98088_put_eq_enum), + }; + + cfg = pdata->eq_cfg; + cfgcnt = pdata->eq_cfgcnt; + + /* Setup an array of texts for the equalizer enum. + * This is based on Mark Brown's equalizer driver code. + */ + max98088->eq_textcnt = 0; + max98088->eq_texts = NULL; + for (i = 0; i < cfgcnt; i++) { + for (j = 0; j < max98088->eq_textcnt; j++) { + if (strcmp(cfg[i].name, max98088->eq_texts[j]) == 0) + break; + } + + if (j != max98088->eq_textcnt) + continue; + + /* Expand the array */ + t = krealloc(max98088->eq_texts, + sizeof(char *) * (max98088->eq_textcnt + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* Store the new entry */ + t[max98088->eq_textcnt] = cfg[i].name; + max98088->eq_textcnt++; + max98088->eq_texts = t; + } + + /* Now point the soc_enum to .texts array items */ + max98088->eq_enum.texts = max98088->eq_texts; + max98088->eq_enum.max = max98088->eq_textcnt; + + ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(codec->dev, "Failed to add EQ control: %d\n", ret); +} + +static void max98088_handle_pdata(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_pdata *pdata = max98088->pdata; + u8 regval = 0; + + if (!pdata) { + dev_dbg(codec->dev, "No platform data\n"); + return; + } + + /* Configure mic for analog/digital mic mode */ + if (pdata->digmic_left_mode) + regval |= M98088_DIGMIC_L; + + if (pdata->digmic_right_mode) + regval |= M98088_DIGMIC_R; + + max98088->digmic = (regval ? 1 : 0); + + snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval); + + /* Configure receiver output */ + regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0); + snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL, + M98088_REC_LINEMODE_MASK, regval); + + /* Configure equalizers */ + if (pdata->eq_cfgcnt) + max98088_handle_eq_pdata(codec); +} + +#ifdef CONFIG_PM +static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + max98088_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max98088_resume(struct snd_soc_codec *codec) +{ + max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max98088_suspend NULL +#define max98088_resume NULL +#endif + +static int max98088_probe(struct snd_soc_codec *codec) +{ + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + struct max98088_cdata *cdata; + int ret = 0; + + codec->cache_sync = 1; + memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg)); + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* initalize private data */ + + max98088->sysclk = (unsigned)-1; + max98088->eq_textcnt = 0; + + cdata = &max98088->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + + cdata = &max98088->dai[1]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + + max98088->ina_state = 0; + max98088->inb_state = 0; + max98088->ex_mode = 0; + max98088->digmic = 0; + max98088->mic1pre = 0; + max98088->mic2pre = 0; + + ret = snd_soc_read(codec, M98088_REG_FF_REV_ID); + if (ret < 0) { + dev_err(codec->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + dev_info(codec->dev, "revision %c\n", ret + 'A'); + + snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV); + + /* initialize registers cache to hardware default */ + max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00); + + snd_soc_write(codec, M98088_REG_22_MIX_DAC, + M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL| + M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR); + + snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0); + snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F); + + snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG, + M98088_S1NORMAL|M98088_SDATA); + + snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG, + M98088_S2NORMAL|M98088_SDATA); + + max98088_handle_pdata(codec); + + max98088_add_widgets(codec); + +err_access: + return ret; +} + +static int max98088_remove(struct snd_soc_codec *codec) +{ + max98088_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max98088 = { + .probe = max98088_probe, + .remove = max98088_remove, + .suspend = max98088_suspend, + .resume = max98088_resume, + .set_bias_level = max98088_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max98088_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = max98088_reg, + .volatile_register = max98088_volatile_register, +}; + +static int max98088_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max98088_priv *max98088; + int ret; + + max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL); + if (max98088 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98088); + max98088->control_data = i2c; + max98088->pdata = i2c->dev.platform_data; + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max98088, &max98088_dai[0], 2); + if (ret < 0) + kfree(max98088); + return ret; +} + +static int __devexit max98088_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id max98088_i2c_id[] = { + { "max98088", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); + +static struct i2c_driver max98088_i2c_driver = { + .driver = { + .name = "max98088", + .owner = THIS_MODULE, + }, + .probe = max98088_i2c_probe, + .remove = __devexit_p(max98088_i2c_remove), + .id_table = max98088_i2c_id, +}; + +static int __init max98088_init(void) +{ + int ret; + + ret = i2c_add_driver(&max98088_i2c_driver); + if (ret) + pr_err("Failed to register max98088 I2C driver: %d\n", ret); + + return ret; +} +module_init(max98088_init); + +static void __exit max98088_exit(void) +{ + i2c_del_driver(&max98088_i2c_driver); +} +module_exit(max98088_exit); + +MODULE_DESCRIPTION("ALSA SoC MAX98088 driver"); +MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h new file mode 100644 index 00000000000..56554c797fe --- /dev/null +++ b/sound/soc/codecs/max98088.h @@ -0,0 +1,193 @@ +/* + * max98088.h -- MAX98088 ALSA SoC Audio driver + * + * Copyright 2010 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98088_H +#define _MAX98088_H + +/* + * MAX98088 Registers Definition + */ +#define M98088_REG_00_IRQ_STATUS 0x00 +#define M98088_REG_01_MIC_STATUS 0x01 +#define M98088_REG_02_JACK_STAUS 0x02 +#define M98088_REG_03_BATTERY_VOLTAGE 0x03 +#define M98088_REG_0F_IRQ_ENABLE 0x0F +#define M98088_REG_10_SYS_CLK 0x10 +#define M98088_REG_11_DAI1_CLKMODE 0x11 +#define M98088_REG_12_DAI1_CLKCFG_HI 0x12 +#define M98088_REG_13_DAI1_CLKCFG_LO 0x13 +#define M98088_REG_14_DAI1_FORMAT 0x14 +#define M98088_REG_15_DAI1_CLOCK 0x15 +#define M98088_REG_16_DAI1_IOCFG 0x16 +#define M98088_REG_17_DAI1_TDM 0x17 +#define M98088_REG_18_DAI1_FILTERS 0x18 +#define M98088_REG_19_DAI2_CLKMODE 0x19 +#define M98088_REG_1A_DAI2_CLKCFG_HI 0x1A +#define M98088_REG_1B_DAI2_CLKCFG_LO 0x1B +#define M98088_REG_1C_DAI2_FORMAT 0x1C +#define M98088_REG_1D_DAI2_CLOCK 0x1D +#define M98088_REG_1E_DAI2_IOCFG 0x1E +#define M98088_REG_1F_DAI2_TDM 0x1F +#define M98088_REG_20_DAI2_FILTERS 0x20 +#define M98088_REG_21_SRC 0x21 +#define M98088_REG_22_MIX_DAC 0x22 +#define M98088_REG_23_MIX_ADC_LEFT 0x23 +#define M98088_REG_24_MIX_ADC_RIGHT 0x24 +#define M98088_REG_25_MIX_HP_LEFT 0x25 +#define M98088_REG_26_MIX_HP_RIGHT 0x26 +#define M98088_REG_27_MIX_HP_CNTL 0x27 +#define M98088_REG_28_MIX_REC_LEFT 0x28 +#define M98088_REG_29_MIX_REC_RIGHT 0x29 +#define M98088_REG_2A_MIC_REC_CNTL 0x2A +#define M98088_REG_2B_MIX_SPK_LEFT 0x2B +#define M98088_REG_2C_MIX_SPK_RIGHT 0x2C +#define M98088_REG_2D_MIX_SPK_CNTL 0x2D +#define M98088_REG_2E_LVL_SIDETONE 0x2E +#define M98088_REG_2F_LVL_DAI1_PLAY 0x2F +#define M98088_REG_30_LVL_DAI1_PLAY_EQ 0x30 +#define M98088_REG_31_LVL_DAI2_PLAY 0x31 +#define M98088_REG_32_LVL_DAI2_PLAY_EQ 0x32 +#define M98088_REG_33_LVL_ADC_L 0x33 +#define M98088_REG_34_LVL_ADC_R 0x34 +#define M98088_REG_35_LVL_MIC1 0x35 +#define M98088_REG_36_LVL_MIC2 0x36 +#define M98088_REG_37_LVL_INA 0x37 +#define M98088_REG_38_LVL_INB 0x38 +#define M98088_REG_39_LVL_HP_L 0x39 +#define M98088_REG_3A_LVL_HP_R 0x3A +#define M98088_REG_3B_LVL_REC_L 0x3B +#define M98088_REG_3C_LVL_REC_R 0x3C +#define M98088_REG_3D_LVL_SPK_L 0x3D +#define M98088_REG_3E_LVL_SPK_R 0x3E +#define M98088_REG_3F_MICAGC_CFG 0x3F +#define M98088_REG_40_MICAGC_THRESH 0x40 +#define M98088_REG_41_SPKDHP 0x41 +#define M98088_REG_42_SPKDHP_THRESH 0x42 +#define M98088_REG_43_SPKALC_COMP 0x43 +#define M98088_REG_44_PWRLMT_CFG 0x44 +#define M98088_REG_45_PWRLMT_TIME 0x45 +#define M98088_REG_46_THDLMT_CFG 0x46 +#define M98088_REG_47_CFG_AUDIO_IN 0x47 +#define M98088_REG_48_CFG_MIC 0x48 +#define M98088_REG_49_CFG_LEVEL 0x49 +#define M98088_REG_4A_CFG_BYPASS 0x4A +#define M98088_REG_4B_CFG_JACKDET 0x4B +#define M98088_REG_4C_PWR_EN_IN 0x4C +#define M98088_REG_4D_PWR_EN_OUT 0x4D +#define M98088_REG_4E_BIAS_CNTL 0x4E +#define M98088_REG_4F_DAC_BIAS1 0x4F +#define M98088_REG_50_DAC_BIAS2 0x50 +#define M98088_REG_51_PWR_SYS 0x51 +#define M98088_REG_52_DAI1_EQ_BASE 0x52 +#define M98088_REG_84_DAI2_EQ_BASE 0x84 +#define M98088_REG_B6_DAI1_BIQUAD_BASE 0xB6 +#define M98088_REG_C0_DAI2_BIQUAD_BASE 0xC0 +#define M98088_REG_FF_REV_ID 0xFF + +#define M98088_REG_CNT (0xFF+1) + +/* MAX98088 Registers Bit Fields */ + +/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */ + #define M98088_CLKMODE_MASK 0xFF + +/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */ + #define M98088_DAI_MAS (1<<7) + #define M98088_DAI_WCI (1<<6) + #define M98088_DAI_BCI (1<<5) + #define M98088_DAI_DLY (1<<4) + #define M98088_DAI_TDM (1<<2) + #define M98088_DAI_FSW (1<<1) + #define M98088_DAI_WS (1<<0) + +/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */ + #define M98088_DAI_BSEL64 (1<<0) + #define M98088_DAI_OSR64 (1<<6) + +/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */ + #define M98088_S1NORMAL (1<<6) + #define M98088_S2NORMAL (2<<6) + #define M98088_SDATA (3<<0) + +/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */ + #define M98088_DAI_DHF (1<<3) + +/* M98088_REG_22_MIX_DAC */ + #define M98088_DAI1L_TO_DACL (1<<7) + #define M98088_DAI1R_TO_DACL (1<<6) + #define M98088_DAI2L_TO_DACL (1<<5) + #define M98088_DAI2R_TO_DACL (1<<4) + #define M98088_DAI1L_TO_DACR (1<<3) + #define M98088_DAI1R_TO_DACR (1<<2) + #define M98088_DAI2L_TO_DACR (1<<1) + #define M98088_DAI2R_TO_DACR (1<<0) + +/* M98088_REG_2A_MIC_REC_CNTL */ + #define M98088_REC_LINEMODE (1<<7) + #define M98088_REC_LINEMODE_MASK (1<<7) + +/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */ + #define M98088_MICPRE_MASK (3<<5) + #define M98088_MICPRE_SHIFT 5 + +/* M98088_REG_3A_LVL_HP_R */ + #define M98088_HP_MUTE (1<<7) + +/* M98088_REG_3C_LVL_REC_R */ + #define M98088_REC_MUTE (1<<7) + +/* M98088_REG_3E_LVL_SPK_R */ + #define M98088_SP_MUTE (1<<7) + +/* M98088_REG_48_CFG_MIC */ + #define M98088_EXTMIC_MASK (3<<0) + #define M98088_DIGMIC_L (1<<5) + #define M98088_DIGMIC_R (1<<4) + +/* M98088_REG_49_CFG_LEVEL */ + #define M98088_VSEN (1<<6) + #define M98088_ZDEN (1<<5) + #define M98088_EQ2EN (1<<1) + #define M98088_EQ1EN (1<<0) + +/* M98088_REG_4C_PWR_EN_IN */ + #define M98088_INAEN (1<<7) + #define M98088_INBEN (1<<6) + #define M98088_MBEN (1<<3) + #define M98088_ADLEN (1<<1) + #define M98088_ADREN (1<<0) + +/* M98088_REG_4D_PWR_EN_OUT */ + #define M98088_HPLEN (1<<7) + #define M98088_HPREN (1<<6) + #define M98088_HPEN ((1<<7)|(1<<6)) + #define M98088_SPLEN (1<<5) + #define M98088_SPREN (1<<4) + #define M98088_RECEN (1<<3) + #define M98088_DALEN (1<<1) + #define M98088_DAREN (1<<0) + +/* M98088_REG_51_PWR_SYS */ + #define M98088_SHDNRUN (1<<7) + #define M98088_PERFMODE (1<<3) + #define M98088_HPPLYBACK (1<<2) + #define M98088_PWRSV8K (1<<1) + #define M98088_PWRSV (1<<0) + +/* Line inputs */ +#define LINE_INA 0 +#define LINE_INB 1 + +#define M98088_COEFS_PER_BAND 5 + +#define M98088_BYTE1(w) ((w >> 8) & 0xff) +#define M98088_BYTE0(w) (w & 0xff) + +#endif diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index ad5987e6c9c..a2e0ed59b37 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -548,11 +548,9 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); unsigned int aifa; unsigned int aifb; int can_invert_lrclk; - int sysclk; aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); @@ -621,8 +619,6 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - sysclk = wm8580->sysclk[codec_dai->driver->id]; - snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 642b07ceddf..4599e8e95aa 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -720,8 +720,8 @@ static int __devinit wm8804_spi_probe(struct spi_device *spi) int ret; wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); - if (IS_ERR(wm8804)) - return PTR_ERR(wm8804); + if (!wm8804) + return -ENOMEM; wm8804->control_type = SND_SOC_SPI; spi_set_drvdata(spi, wm8804); @@ -758,8 +758,8 @@ static __devinit int wm8804_i2c_probe(struct i2c_client *i2c, int ret; wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); - if (IS_ERR(wm8804)) - return PTR_ERR(wm8804); + if (!wm8804) + return -ENOMEM; wm8804->control_type = SND_SOC_I2C; i2c_set_clientdata(i2c, wm8804); diff --git a/sound/soc/codecs/wm8962-tables.c b/sound/soc/codecs/wm8962-tables.c deleted file mode 100644 index 6fa98ea0ae4..00000000000 --- a/sound/soc/codecs/wm8962-tables.c +++ /dev/null @@ -1,42299 +0,0 @@ -/* - * wm8962-tables.c -- WM8962 ASoC driver - * - * Copyright 2010 Wolfson Microelectronics, plc - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include "wm8962.h" - -const u16 wm8962_reg[WM8962_MAX_REGISTER + 1] = { - 0x009F, /* R0 - Left Input volume */ - 0x049F, /* R1 - Right Input volume */ - 0x0000, /* R2 - HPOUTL volume */ - 0x0000, /* R3 - HPOUTR volume */ - 0x0020, /* R4 - Clocking1 */ - 0x0018, /* R5 - ADC & DAC Control 1 */ - 0x2008, /* R6 - ADC & DAC Control 2 */ - 0x000A, /* R7 - Audio Interface 0 */ - 0x01E4, /* R8 - Clocking2 */ - 0x0300, /* R9 - Audio Interface 1 */ - 0x00C0, /* R10 - Left DAC volume */ - 0x00C0, /* R11 - Right DAC volume */ - 0x0000, /* R12 */ - 0x0000, /* R13 */ - 0x0040, /* R14 - Audio Interface 2 */ - 0x6243, /* R15 - Software Reset */ - 0x0000, /* R16 */ - 0x007B, /* R17 - ALC1 */ - 0x0000, /* R18 - ALC2 */ - 0x1C32, /* R19 - ALC3 */ - 0x3200, /* R20 - Noise Gate */ - 0x00C0, /* R21 - Left ADC volume */ - 0x00C0, /* R22 - Right ADC volume */ - 0x0160, /* R23 - Additional control(1) */ - 0x0000, /* R24 - Additional control(2) */ - 0x0000, /* R25 - Pwr Mgmt (1) */ - 0x0000, /* R26 - Pwr Mgmt (2) */ - 0x0010, /* R27 - Additional Control (3) */ - 0x0000, /* R28 - Anti-pop */ - 0x0000, /* R29 */ - 0x005E, /* R30 - Clocking 3 */ - 0x0000, /* R31 - Input mixer control (1) */ - 0x0145, /* R32 - Left input mixer volume */ - 0x0145, /* R33 - Right input mixer volume */ - 0x0009, /* R34 - Input mixer control (2) */ - 0x0003, /* R35 - Input bias control */ - 0x0000, /* R36 */ - 0x0008, /* R37 - Left input PGA control */ - 0x0008, /* R38 - Right input PGA control */ - 0x0000, /* R39 */ - 0x0000, /* R40 - SPKOUTL volume */ - 0x0000, /* R41 - SPKOUTR volume */ - 0x0000, /* R42 */ - 0x0000, /* R43 */ - 0x0000, /* R44 */ - 0x0000, /* R45 */ - 0x0000, /* R46 */ - 0x0000, /* R47 - Thermal Shutdown Status */ - 0x8027, /* R48 - Additional Control (4) */ - 0x0010, /* R49 - Class D Control 1 */ - 0x0000, /* R50 */ - 0x0003, /* R51 - Class D Control 2 */ - 0x0000, /* R52 */ - 0x0000, /* R53 */ - 0x0000, /* R54 */ - 0x0000, /* R55 */ - 0x0506, /* R56 - Clocking 4 */ - 0x0000, /* R57 - DAC DSP Mixing (1) */ - 0x0000, /* R58 - DAC DSP Mixing (2) */ - 0x0000, /* R59 */ - 0x0300, /* R60 - DC Servo 0 */ - 0x0300, /* R61 - DC Servo 1 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x0810, /* R64 - DC Servo 4 */ - 0x0000, /* R65 */ - 0x0000, /* R66 - DC Servo 6 */ - 0x0000, /* R67 */ - 0x001B, /* R68 - Analogue PGA Bias */ - 0x0000, /* R69 - Analogue HP 0 */ - 0x0000, /* R70 */ - 0x01FB, /* R71 - Analogue HP 2 */ - 0x0000, /* R72 - Charge Pump 1 */ - 0x0000, /* R73 */ - 0x0000, /* R74 */ - 0x0000, /* R75 */ - 0x0000, /* R76 */ - 0x0000, /* R77 */ - 0x0000, /* R78 */ - 0x0000, /* R79 */ - 0x0000, /* R80 */ - 0x0000, /* R81 */ - 0x0004, /* R82 - Charge Pump B */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 - Write Sequencer Control 1 */ - 0x0000, /* R88 */ - 0x0000, /* R89 */ - 0x0000, /* R90 - Write Sequencer Control 2 */ - 0x0000, /* R91 */ - 0x0000, /* R92 */ - 0x0000, /* R93 - Write Sequencer Control 3 */ - 0x0000, /* R94 - Control Interface */ - 0x0000, /* R95 */ - 0x0000, /* R96 */ - 0x0000, /* R97 */ - 0x0000, /* R98 */ - 0x0000, /* R99 - Mixer Enables */ - 0x0000, /* R100 - Headphone Mixer (1) */ - 0x0000, /* R101 - Headphone Mixer (2) */ - 0x013F, /* R102 - Headphone Mixer (3) */ - 0x013F, /* R103 - Headphone Mixer (4) */ - 0x0000, /* R104 */ - 0x0000, /* R105 - Speaker Mixer (1) */ - 0x0000, /* R106 - Speaker Mixer (2) */ - 0x013F, /* R107 - Speaker Mixer (3) */ - 0x013F, /* R108 - Speaker Mixer (4) */ - 0x0003, /* R109 - Speaker Mixer (5) */ - 0x0002, /* R110 - Beep Generator (1) */ - 0x0000, /* R111 */ - 0x0000, /* R112 */ - 0x0000, /* R113 */ - 0x0000, /* R114 */ - 0x0006, /* R115 - Oscillator Trim (3) */ - 0x0026, /* R116 - Oscillator Trim (4) */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 - Oscillator Trim (7) */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0011, /* R124 - Analogue Clocking1 */ - 0x004B, /* R125 - Analogue Clocking2 */ - 0x000D, /* R126 - Analogue Clocking3 */ - 0x0000, /* R127 - PLL Software Reset */ - 0x0000, /* R128 */ - 0x0000, /* R129 - PLL2 */ - 0x0000, /* R130 */ - 0x0000, /* R131 - PLL 4 */ - 0x0000, /* R132 */ - 0x0000, /* R133 */ - 0x0000, /* R134 */ - 0x0000, /* R135 */ - 0x0067, /* R136 - PLL 9 */ - 0x001C, /* R137 - PLL 10 */ - 0x0071, /* R138 - PLL 11 */ - 0x00C7, /* R139 - PLL 12 */ - 0x0067, /* R140 - PLL 13 */ - 0x0048, /* R141 - PLL 14 */ - 0x0022, /* R142 - PLL 15 */ - 0x0097, /* R143 - PLL 16 */ - 0x0000, /* R144 */ - 0x0000, /* R145 */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x0000, /* R152 */ - 0x0000, /* R153 */ - 0x0000, /* R154 */ - 0x000C, /* R155 - FLL Control (1) */ - 0x0039, /* R156 - FLL Control (2) */ - 0x0180, /* R157 - FLL Control (3) */ - 0x0000, /* R158 */ - 0x0032, /* R159 - FLL Control (5) */ - 0x0018, /* R160 - FLL Control (6) */ - 0x007D, /* R161 - FLL Control (7) */ - 0x0008, /* R162 - FLL Control (8) */ - 0x0000, /* R163 */ - 0x0000, /* R164 */ - 0x0000, /* R165 */ - 0x0000, /* R166 */ - 0x0000, /* R167 */ - 0x0000, /* R168 */ - 0x0000, /* R169 */ - 0x0000, /* R170 */ - 0x0000, /* R171 */ - 0x0000, /* R172 */ - 0x0000, /* R173 */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 */ - 0x0000, /* R177 */ - 0x0000, /* R178 */ - 0x0000, /* R179 */ - 0x0000, /* R180 */ - 0x0000, /* R181 */ - 0x0000, /* R182 */ - 0x0000, /* R183 */ - 0x0000, /* R184 */ - 0x0000, /* R185 */ - 0x0000, /* R186 */ - 0x0000, /* R187 */ - 0x0000, /* R188 */ - 0x0000, /* R189 */ - 0x0000, /* R190 */ - 0x0000, /* R191 */ - 0x0000, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0000, /* R195 */ - 0x0000, /* R196 */ - 0x0000, /* R197 */ - 0x0000, /* R198 */ - 0x0000, /* R199 */ - 0x0000, /* R200 */ - 0x0000, /* R201 */ - 0x0000, /* R202 */ - 0x0000, /* R203 */ - 0x0000, /* R204 */ - 0x0000, /* R205 */ - 0x0000, /* R206 */ - 0x0000, /* R207 */ - 0x0000, /* R208 */ - 0x0000, /* R209 */ - 0x0000, /* R210 */ - 0x0000, /* R211 */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 */ - 0x0000, /* R216 */ - 0x0000, /* R217 */ - 0x0000, /* R218 */ - 0x0000, /* R219 */ - 0x0000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0x0000, /* R230 */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0005, /* R252 - General test 1 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ - 0x0000, /* R256 - DF1 */ - 0x0000, /* R257 - DF2 */ - 0x0000, /* R258 - DF3 */ - 0x0000, /* R259 - DF4 */ - 0x0000, /* R260 - DF5 */ - 0x0000, /* R261 - DF6 */ - 0x0000, /* R262 - DF7 */ - 0x0000, /* R263 */ - 0x0000, /* R264 - LHPF1 */ - 0x0000, /* R265 - LHPF2 */ - 0x0000, /* R266 */ - 0x0000, /* R267 */ - 0x0000, /* R268 - THREED1 */ - 0x0000, /* R269 - THREED2 */ - 0x0000, /* R270 - THREED3 */ - 0x0000, /* R271 - THREED4 */ - 0x0000, /* R272 */ - 0x0000, /* R273 */ - 0x0000, /* R274 */ - 0x0000, /* R275 */ - 0x000C, /* R276 - DRC 1 */ - 0x0925, /* R277 - DRC 2 */ - 0x0000, /* R278 - DRC 3 */ - 0x0000, /* R279 - DRC 4 */ - 0x0000, /* R280 - DRC 5 */ - 0x0000, /* R281 */ - 0x0000, /* R282 */ - 0x0000, /* R283 */ - 0x0000, /* R284 */ - 0x0000, /* R285 - Tloopback */ - 0x0000, /* R286 */ - 0x0000, /* R287 */ - 0x0000, /* R288 */ - 0x0000, /* R289 */ - 0x0000, /* R290 */ - 0x0000, /* R291 */ - 0x0000, /* R292 */ - 0x0000, /* R293 */ - 0x0000, /* R294 */ - 0x0000, /* R295 */ - 0x0000, /* R296 */ - 0x0000, /* R297 */ - 0x0000, /* R298 */ - 0x0000, /* R299 */ - 0x0000, /* R300 */ - 0x0000, /* R301 */ - 0x0000, /* R302 */ - 0x0000, /* R303 */ - 0x0000, /* R304 */ - 0x0000, /* R305 */ - 0x0000, /* R306 */ - 0x0000, /* R307 */ - 0x0000, /* R308 */ - 0x0000, /* R309 */ - 0x0000, /* R310 */ - 0x0000, /* R311 */ - 0x0000, /* R312 */ - 0x0000, /* R313 */ - 0x0000, /* R314 */ - 0x0000, /* R315 */ - 0x0000, /* R316 */ - 0x0000, /* R317 */ - 0x0000, /* R318 */ - 0x0000, /* R319 */ - 0x0000, /* R320 */ - 0x0000, /* R321 */ - 0x0000, /* R322 */ - 0x0000, /* R323 */ - 0x0000, /* R324 */ - 0x0000, /* R325 */ - 0x0000, /* R326 */ - 0x0000, /* R327 */ - 0x0000, /* R328 */ - 0x0000, /* R329 */ - 0x0000, /* R330 */ - 0x0000, /* R331 */ - 0x0000, /* R332 */ - 0x0000, /* R333 */ - 0x0000, /* R334 */ - 0x0004, /* R335 - EQ1 */ - 0x6318, /* R336 - EQ2 */ - 0x6300, /* R337 - EQ3 */ - 0x0FCA, /* R338 - EQ4 */ - 0x0400, /* R339 - EQ5 */ - 0x00D8, /* R340 - EQ6 */ - 0x1EB5, /* R341 - EQ7 */ - 0xF145, /* R342 - EQ8 */ - 0x0B75, /* R343 - EQ9 */ - 0x01C5, /* R344 - EQ10 */ - 0x1C58, /* R345 - EQ11 */ - 0xF373, /* R346 - EQ12 */ - 0x0A54, /* R347 - EQ13 */ - 0x0558, /* R348 - EQ14 */ - 0x168E, /* R349 - EQ15 */ - 0xF829, /* R350 - EQ16 */ - 0x07AD, /* R351 - EQ17 */ - 0x1103, /* R352 - EQ18 */ - 0x0564, /* R353 - EQ19 */ - 0x0559, /* R354 - EQ20 */ - 0x4000, /* R355 - EQ21 */ - 0x6318, /* R356 - EQ22 */ - 0x6300, /* R357 - EQ23 */ - 0x0FCA, /* R358 - EQ24 */ - 0x0400, /* R359 - EQ25 */ - 0x00D8, /* R360 - EQ26 */ - 0x1EB5, /* R361 - EQ27 */ - 0xF145, /* R362 - EQ28 */ - 0x0B75, /* R363 - EQ29 */ - 0x01C5, /* R364 - EQ30 */ - 0x1C58, /* R365 - EQ31 */ - 0xF373, /* R366 - EQ32 */ - 0x0A54, /* R367 - EQ33 */ - 0x0558, /* R368 - EQ34 */ - 0x168E, /* R369 - EQ35 */ - 0xF829, /* R370 - EQ36 */ - 0x07AD, /* R371 - EQ37 */ - 0x1103, /* R372 - EQ38 */ - 0x0564, /* R373 - EQ39 */ - 0x0559, /* R374 - EQ40 */ - 0x4000, /* R375 - EQ41 */ - 0x0000, /* R376 */ - 0x0000, /* R377 */ - 0x0000, /* R378 */ - 0x0000, /* R379 */ - 0x0000, /* R380 */ - 0x0000, /* R381 */ - 0x0000, /* R382 */ - 0x0000, /* R383 */ - 0x0000, /* R384 */ - 0x0000, /* R385 */ - 0x0000, /* R386 */ - 0x0000, /* R387 */ - 0x0000, /* R388 */ - 0x0000, /* R389 */ - 0x0000, /* R390 */ - 0x0000, /* R391 */ - 0x0000, /* R392 */ - 0x0000, /* R393 */ - 0x0000, /* R394 */ - 0x0000, /* R395 */ - 0x0000, /* R396 */ - 0x0000, /* R397 */ - 0x0000, /* R398 */ - 0x0000, /* R399 */ - 0x0000, /* R400 */ - 0x0000, /* R401 */ - 0x0000, /* R402 */ - 0x0000, /* R403 */ - 0x0000, /* R404 */ - 0x0000, /* R405 */ - 0x0000, /* R406 */ - 0x0000, /* R407 */ - 0x0000, /* R408 */ - 0x0000, /* R409 */ - 0x0000, /* R410 */ - 0x0000, /* R411 */ - 0x0000, /* R412 */ - 0x0000, /* R413 */ - 0x0000, /* R414 */ - 0x0000, /* R415 */ - 0x0000, /* R416 */ - 0x0000, /* R417 */ - 0x0000, /* R418 */ - 0x0000, /* R419 */ - 0x0000, /* R420 */ - 0x0000, /* R421 */ - 0x0000, /* R422 */ - 0x0000, /* R423 */ - 0x0000, /* R424 */ - 0x0000, /* R425 */ - 0x0000, /* R426 */ - 0x0000, /* R427 */ - 0x0000, /* R428 */ - 0x0000, /* R429 */ - 0x0000, /* R430 */ - 0x0000, /* R431 */ - 0x0000, /* R432 */ - 0x0000, /* R433 */ - 0x0000, /* R434 */ - 0x0000, /* R435 */ - 0x0000, /* R436 */ - 0x0000, /* R437 */ - 0x0000, /* R438 */ - 0x0000, /* R439 */ - 0x0000, /* R440 */ - 0x0000, /* R441 */ - 0x0000, /* R442 */ - 0x0000, /* R443 */ - 0x0000, /* R444 */ - 0x0000, /* R445 */ - 0x0000, /* R446 */ - 0x0000, /* R447 */ - 0x0000, /* R448 */ - 0x0000, /* R449 */ - 0x0000, /* R450 */ - 0x0000, /* R451 */ - 0x0000, /* R452 */ - 0x0000, /* R453 */ - 0x0000, /* R454 */ - 0x0000, /* R455 */ - 0x0000, /* R456 */ - 0x0000, /* R457 */ - 0x0000, /* R458 */ - 0x0000, /* R459 */ - 0x0000, /* R460 */ - 0x0000, /* R461 */ - 0x0000, /* R462 */ - 0x0000, /* R463 */ - 0x0000, /* R464 */ - 0x0000, /* R465 */ - 0x0000, /* R466 */ - 0x0000, /* R467 */ - 0x0000, /* R468 */ - 0x0000, /* R469 */ - 0x0000, /* R470 */ - 0x0000, /* R471 */ - 0x0000, /* R472 */ - 0x0000, /* R473 */ - 0x0000, /* R474 */ - 0x0000, /* R475 */ - 0x0000, /* R476 */ - 0x0000, /* R477 */ - 0x0000, /* R478 */ - 0x0000, /* R479 */ - 0x0000, /* R480 */ - 0x0000, /* R481 */ - 0x0000, /* R482 */ - 0x0000, /* R483 */ - 0x0000, /* R484 */ - 0x0000, /* R485 */ - 0x0000, /* R486 */ - 0x0000, /* R487 */ - 0x0000, /* R488 */ - 0x0000, /* R489 */ - 0x0000, /* R490 */ - 0x0000, /* R491 */ - 0x0000, /* R492 */ - 0x0000, /* R493 */ - 0x0000, /* R494 */ - 0x0000, /* R495 */ - 0x0000, /* R496 */ - 0x0000, /* R497 */ - 0x0000, /* R498 */ - 0x0000, /* R499 */ - 0x0000, /* R500 */ - 0x0000, /* R501 */ - 0x0000, /* R502 */ - 0x0000, /* R503 */ - 0x0000, /* R504 */ - 0x0000, /* R505 */ - 0x0000, /* R506 */ - 0x0000, /* R507 */ - 0x0000, /* R508 */ - 0x0000, /* R509 */ - 0x0000, /* R510 */ - 0x0000, /* R511 */ - 0x0000, /* R512 */ - 0x0000, /* R513 - GPIO 2 */ - 0x0000, /* R514 - GPIO 3 */ - 0x0000, /* R515 */ - 0x8100, /* R516 - GPIO 5 */ - 0x8100, /* R517 - GPIO 6 */ - 0x0000, /* R518 */ - 0x0000, /* R519 */ - 0x0000, /* R520 */ - 0x0000, /* R521 */ - 0x0000, /* R522 */ - 0x0000, /* R523 */ - 0x0000, /* R524 */ - 0x0000, /* R525 */ - 0x0000, /* R526 */ - 0x0000, /* R527 */ - 0x0000, /* R528 */ - 0x0000, /* R529 */ - 0x0000, /* R530 */ - 0x0000, /* R531 */ - 0x0000, /* R532 */ - 0x0000, /* R533 */ - 0x0000, /* R534 */ - 0x0000, /* R535 */ - 0x0000, /* R536 */ - 0x0000, /* R537 */ - 0x0000, /* R538 */ - 0x0000, /* R539 */ - 0x0000, /* R540 */ - 0x0000, /* R541 */ - 0x0000, /* R542 */ - 0x0000, /* R543 */ - 0x0000, /* R544 */ - 0x0000, /* R545 */ - 0x0000, /* R546 */ - 0x0000, /* R547 */ - 0x0000, /* R548 */ - 0x0000, /* R549 */ - 0x0000, /* R550 */ - 0x0000, /* R551 */ - 0x0000, /* R552 */ - 0x0000, /* R553 */ - 0x0000, /* R554 */ - 0x0000, /* R555 */ - 0x0000, /* R556 */ - 0x0000, /* R557 */ - 0x0000, /* R558 */ - 0x0000, /* R559 */ - 0x0000, /* R560 - Interrupt Status 1 */ - 0x0000, /* R561 - Interrupt Status 2 */ - 0x0000, /* R562 */ - 0x0000, /* R563 */ - 0x0000, /* R564 */ - 0x0000, /* R565 */ - 0x0000, /* R566 */ - 0x0000, /* R567 */ - 0x0030, /* R568 - Interrupt Status 1 Mask */ - 0xFFED, /* R569 - Interrupt Status 2 Mask */ - 0x0000, /* R570 */ - 0x0000, /* R571 */ - 0x0000, /* R572 */ - 0x0000, /* R573 */ - 0x0000, /* R574 */ - 0x0000, /* R575 */ - 0x0000, /* R576 - Interrupt Control */ - 0x0000, /* R577 */ - 0x0000, /* R578 */ - 0x0000, /* R579 */ - 0x0000, /* R580 */ - 0x0000, /* R581 */ - 0x0000, /* R582 */ - 0x0000, /* R583 */ - 0x002D, /* R584 - IRQ Debounce */ - 0x0000, /* R585 */ - 0x0000, /* R586 - MICINT Source Pol */ - 0x0000, /* R587 */ - 0x0000, /* R588 */ - 0x0000, /* R589 */ - 0x0000, /* R590 */ - 0x0000, /* R591 */ - 0x0000, /* R592 */ - 0x0000, /* R593 */ - 0x0000, /* R594 */ - 0x0000, /* R595 */ - 0x0000, /* R596 */ - 0x0000, /* R597 */ - 0x0000, /* R598 */ - 0x0000, /* R599 */ - 0x0000, /* R600 */ - 0x0000, /* R601 */ - 0x0000, /* R602 */ - 0x0000, /* R603 */ - 0x0000, /* R604 */ - 0x0000, /* R605 */ - 0x0000, /* R606 */ - 0x0000, /* R607 */ - 0x0000, /* R608 */ - 0x0000, /* R609 */ - 0x0000, /* R610 */ - 0x0000, /* R611 */ - 0x0000, /* R612 */ - 0x0000, /* R613 */ - 0x0000, /* R614 */ - 0x0000, /* R615 */ - 0x0000, /* R616 */ - 0x0000, /* R617 */ - 0x0000, /* R618 */ - 0x0000, /* R619 */ - 0x0000, /* R620 */ - 0x0000, /* R621 */ - 0x0000, /* R622 */ - 0x0000, /* R623 */ - 0x0000, /* R624 */ - 0x0000, /* R625 */ - 0x0000, /* R626 */ - 0x0000, /* R627 */ - 0x0000, /* R628 */ - 0x0000, /* R629 */ - 0x0000, /* R630 */ - 0x0000, /* R631 */ - 0x0000, /* R632 */ - 0x0000, /* R633 */ - 0x0000, /* R634 */ - 0x0000, /* R635 */ - 0x0000, /* R636 */ - 0x0000, /* R637 */ - 0x0000, /* R638 */ - 0x0000, /* R639 */ - 0x0000, /* R640 */ - 0x0000, /* R641 */ - 0x0000, /* R642 */ - 0x0000, /* R643 */ - 0x0000, /* R644 */ - 0x0000, /* R645 */ - 0x0000, /* R646 */ - 0x0000, /* R647 */ - 0x0000, /* R648 */ - 0x0000, /* R649 */ - 0x0000, /* R650 */ - 0x0000, /* R651 */ - 0x0000, /* R652 */ - 0x0000, /* R653 */ - 0x0000, /* R654 */ - 0x0000, /* R655 */ - 0x0000, /* R656 */ - 0x0000, /* R657 */ - 0x0000, /* R658 */ - 0x0000, /* R659 */ - 0x0000, /* R660 */ - 0x0000, /* R661 */ - 0x0000, /* R662 */ - 0x0000, /* R663 */ - 0x0000, /* R664 */ - 0x0000, /* R665 */ - 0x0000, /* R666 */ - 0x0000, /* R667 */ - 0x0000, /* R668 */ - 0x0000, /* R669 */ - 0x0000, /* R670 */ - 0x0000, /* R671 */ - 0x0000, /* R672 */ - 0x0000, /* R673 */ - 0x0000, /* R674 */ - 0x0000, /* R675 */ - 0x0000, /* R676 */ - 0x0000, /* R677 */ - 0x0000, /* R678 */ - 0x0000, /* R679 */ - 0x0000, /* R680 */ - 0x0000, /* R681 */ - 0x0000, /* R682 */ - 0x0000, /* R683 */ - 0x0000, /* R684 */ - 0x0000, /* R685 */ - 0x0000, /* R686 */ - 0x0000, /* R687 */ - 0x0000, /* R688 */ - 0x0000, /* R689 */ - 0x0000, /* R690 */ - 0x0000, /* R691 */ - 0x0000, /* R692 */ - 0x0000, /* R693 */ - 0x0000, /* R694 */ - 0x0000, /* R695 */ - 0x0000, /* R696 */ - 0x0000, /* R697 */ - 0x0000, /* R698 */ - 0x0000, /* R699 */ - 0x0000, /* R700 */ - 0x0000, /* R701 */ - 0x0000, /* R702 */ - 0x0000, /* R703 */ - 0x0000, /* R704 */ - 0x0000, /* R705 */ - 0x0000, /* R706 */ - 0x0000, /* R707 */ - 0x0000, /* R708 */ - 0x0000, /* R709 */ - 0x0000, /* R710 */ - 0x0000, /* R711 */ - 0x0000, /* R712 */ - 0x0000, /* R713 */ - 0x0000, |