diff options
Diffstat (limited to 'sound')
79 files changed, 4115 insertions, 5362 deletions
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 762af68c8996..270790d384e2 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -1132,15 +1132,4 @@ static struct i2c_driver onyx_driver = { .id_table = onyx_i2c_id, }; -static int __init onyx_init(void) -{ - return i2c_add_driver(&onyx_driver); -} - -static void __exit onyx_exit(void) -{ - i2c_del_driver(&onyx_driver); -} - -module_init(onyx_init); -module_exit(onyx_exit); +module_i2c_driver(onyx_driver); diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index fd2188c3df2b..8e63d1f35ce1 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -1026,15 +1026,4 @@ static struct i2c_driver tas_driver = { .id_table = tas_i2c_id, }; -static int __init tas_init(void) -{ - return i2c_add_driver(&tas_driver); -} - -static void __exit tas_exit(void) -{ - i2c_del_driver(&tas_driver); -} - -module_init(tas_init); -module_exit(tas_exit); +module_i2c_driver(tas_driver); diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index b37b702a3a6a..5119fdabcb98 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -1110,18 +1110,7 @@ static struct amba_driver aaci_driver = { .id_table = aaci_ids, }; -static int __init aaci_init(void) -{ - return amba_driver_register(&aaci_driver); -} - -static void __exit aaci_exit(void) -{ - amba_driver_unregister(&aaci_driver); -} - -module_init(aaci_init); -module_exit(aaci_exit); +module_amba_driver(aaci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver"); diff --git a/sound/core/control.c b/sound/core/control.c index 819a5c579a39..2487a6bb1c54 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1313,7 +1313,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, err = -EPERM; goto __kctl_end; } - err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); + err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); if (err > 0) { up_read(&card->controls_rwsem); snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); diff --git a/sound/core/init.c b/sound/core/init.c index 3ac49b1b7cb8..d8ec849af128 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/module.h> +#include <linux/device.h> #include <linux/file.h> #include <linux/slab.h> #include <linux/time.h> @@ -480,74 +481,104 @@ int snd_card_free(struct snd_card *card) EXPORT_SYMBOL(snd_card_free); -static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) +/* retrieve the last word of shortname or longname */ +static const char *retrieve_id_from_card_name(const char *name) { - int i, len, idx_flag = 0, loops = SNDRV_CARDS; - const char *spos, *src; - char *id; - - if (nid == NULL) { - id = card->shortname; - spos = src = id; - while (*id != '\0') { - if (*id == ' ') - spos = id + 1; - id++; - } - } else { - spos = src = nid; + const char *spos = name; + + while (*name) { + if (isspace(*name) && isalnum(name[1])) + spos = name + 1; + name++; } - id = card->id; - while (*spos != '\0' && !isalnum(*spos)) - spos++; - if (isdigit(*spos)) - *id++ = isalpha(src[0]) ? src[0] : 'D'; - while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { - if (isalnum(*spos)) - *id++ = *spos; - spos++; + return spos; +} + +/* return true if the given id string doesn't conflict any other card ids */ +static bool card_id_ok(struct snd_card *card, const char *id) +{ + int i; + if (!snd_info_check_reserved_words(id)) + return false; + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && snd_cards[i] != card && + !strcmp(snd_cards[i]->id, id)) + return false; } - *id = '\0'; + return true; +} - id = card->id; +/* copy to card->id only with valid letters from nid */ +static void copy_valid_id_string(struct snd_card *card, const char *src, + const char *nid) +{ + char *id = card->id; + + while (*nid && !isalnum(*nid)) + nid++; + if (isdigit(*nid)) + *id++ = isalpha(*src) ? *src : 'D'; + while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { + if (isalnum(*nid)) + *id++ = *nid; + nid++; + } + *id = 0; +} + +/* Set card->id from the given string + * If the string conflicts with other ids, add a suffix to make it unique. + */ +static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, + const char *nid) +{ + int len, loops; + bool with_suffix; + bool is_default = false; + char *id; - if (*id == '\0') + copy_valid_id_string(card, src, nid); + id = card->id; + + again: + /* use "Default" for obviously invalid strings + * ("card" conflicts with proc directories) + */ + if (!*id || !strncmp(id, "card", 4)) { strcpy(id, "Default"); + is_default = true; + } - while (1) { - if (loops-- == 0) { - snd_printk(KERN_ERR "unable to set card id (%s)\n", id); - strcpy(card->id, card->proc_root->name); - return; - } - if (!snd_info_check_reserved_words(id)) - goto __change; - for (i = 0; i < snd_ecards_limit; i++) { - if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) - goto __change; - } - break; + with_suffix = false; + for (loops = 0; loops < SNDRV_CARDS; loops++) { + if (card_id_ok(card, id)) + return; /* OK */ - __change: len = strlen(id); - if (idx_flag) { - if (id[len-1] != '9') - id[len-1]++; - else - id[len-1] = 'A'; - } else if ((size_t)len <= sizeof(card->id) - 3) { - strcat(id, "_1"); - idx_flag++; + if (!with_suffix) { + /* add the "_X" suffix */ + char *spos = id + len; + if (len > sizeof(card->id) - 3) + spos = id + sizeof(card->id) - 3; + strcpy(spos, "_1"); + with_suffix = true; } else { - spos = id + len - 2; - if ((size_t)len <= sizeof(card->id) - 2) - spos++; - *(char *)spos++ = '_'; - *(char *)spos++ = '1'; - *(char *)spos++ = '\0'; - idx_flag++; + /* modify the existing suffix */ + if (id[len - 1] != '9') + id[len - 1]++; + else + id[len - 1] = 'A'; } } + /* fallback to the default id */ + if (!is_default) { + *id = 0; + goto again; + } + /* last resort... */ + snd_printk(KERN_ERR "unable to set card id (%s)\n", id); + if (card->proc_root->name) + strcpy(card->id, card->proc_root->name); } /** @@ -564,7 +595,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid) if (card->id[0] != '\0') return; mutex_lock(&snd_card_mutex); - snd_card_set_id_no_lock(card, nid); + snd_card_set_id_no_lock(card, nid, nid); mutex_unlock(&snd_card_mutex); } EXPORT_SYMBOL(snd_card_set_id); @@ -596,22 +627,12 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, memcpy(buf1, buf, copy); buf1[copy] = '\0'; mutex_lock(&snd_card_mutex); - if (!snd_info_check_reserved_words(buf1)) { - __exist: + if (!card_id_ok(NULL, buf1)) { mutex_unlock(&snd_card_mutex); return -EEXIST; } - for (idx = 0; idx < snd_ecards_limit; idx++) { - 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; @@ -665,7 +686,18 @@ int snd_card_register(struct snd_card *card) mutex_unlock(&snd_card_mutex); return 0; } - snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); + if (*card->id) { + /* make a unique id name from the given string */ + char tmpid[sizeof(card->id)]; + memcpy(tmpid, card->id, sizeof(card->id)); + snd_card_set_id_no_lock(card, tmpid, tmpid); + } else { + /* create an id from either shortname or longname */ + const char *src; + src = *card->shortname ? card->shortname : card->longname; + snd_card_set_id_no_lock(card, src, + retrieve_id_from_card_name(src)); + } snd_cards[card->number] = card; mutex_unlock(&snd_card_mutex); init_info_for_card(card); diff --git a/sound/core/jack.c b/sound/core/jack.c index 26edf63b265f..471e1e3b0a99 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -25,7 +25,7 @@ #include <sound/jack.h> #include <sound/core.h> -static int jack_switch_types[] = { +static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_HEADPHONE_INSERT, SW_MICROPHONE_INSERT, SW_LINEOUT_INSERT, @@ -128,7 +128,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, jack->type = type; - for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) + for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) if (type & (1 << i)) input_set_capability(jack->input_dev, EV_SW, jack_switch_types[i]); diff --git a/sound/core/misc.c b/sound/core/misc.c index 465f0ce772cb..768167925409 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -72,7 +72,7 @@ void __snd_printk(unsigned int level, const char *path, int line, char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; #endif -#ifdef CONFIG_SND_DEBUG +#ifdef CONFIG_SND_DEBUG if (debug < level) return; #endif diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6e4bfcc14254..1a3070b4e5b5 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/device.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/pcm.h> diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 3420bd3da5d7..4d18941178e6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1029,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i, * * Returns non-zero if the value is changed, zero if not changed. */ -int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) +int snd_interval_list(struct snd_interval *i, unsigned int count, + const unsigned int *list, unsigned int mask) { unsigned int k; struct snd_interval list_range; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 25ed9fe41b89..3fe99e644eb8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1586,12 +1586,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) struct file *file; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; + struct snd_pcm_group *group; file = snd_pcm_file_fd(fd); if (!file) return -EBADFD; pcm_file = file->private_data; substream1 = pcm_file->substream; + group = kmalloc(sizeof(*group), GFP_KERNEL); + if (!group) { + res = -ENOMEM; + goto _nolock; + } down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || @@ -1604,11 +1610,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) goto _end; } if (!snd_pcm_stream_linked(substream)) { - substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); - if (substream->group == NULL) { - res = -ENOMEM; - goto _end; - } + substream->group = group; spin_lock_init(&substream->group->lock); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); @@ -1620,7 +1622,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) _end: write_unlock_irq(&snd_pcm_link_rwlock); up_write(&snd_pcm_link_rwsem); + _nolock: fput(file); + if (res < 0) + kfree(group); return res; } diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 9d8379aedf40..712110561082 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/device.h> #include <sound/core.h> #include <sound/initval.h> diff --git a/sound/core/timer.c b/sound/core/timer.c index 8e7561dfc5fc..6ddcf06f52f9 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/device.h> #include <linux/module.h> #include <linux/string.h> #include <sound/core.h> diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 130cfe677d60..14a286a7bf2b 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -37,6 +37,8 @@ struct link_master { struct link_ctl_info info; int val; /* the master value */ unsigned int tlv[4]; + void (*hook)(void *private_data, int); + void *hook_private_data; }; /* @@ -126,7 +128,9 @@ static int master_init(struct link_master *master) master->info.count = 1; /* always mono */ /* set full volume as default (= no attenuation) */ master->val = master->info.max_val; - return 0; + if (master->hook) + master->hook(master->hook_private_data, master->val); + return 1; } return -ENOENT; } @@ -329,6 +333,8 @@ static int master_put(struct snd_kcontrol *kcontrol, slave_put_val(slave, uval); } kfree(uval); + if (master->hook && !err) + master->hook(master->hook_private_data, master->val); return 1; } @@ -408,3 +414,41 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, return kctl; } EXPORT_SYMBOL(snd_ctl_make_virtual_master); + +/** + * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control + * @kcontrol: vmaster kctl element + * @hook: the hook function + * + * Adds the given hook to the vmaster control element so that it's called + * at each time when the value is changed. + */ +int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol, + void (*hook)(void *private_data, int), + void *private_data) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + master->hook = hook; + master->hook_private_data = private_data; + return 0; +} +EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook); + +/** + * snd_ctl_sync_vmaster_hook - Sync the vmaster hook + * @kcontrol: vmaster kctl element + * + * Call the hook function to synchronize with the current value of the given + * vmaster element. NOP when NULL is passed to @kcontrol or the hook doesn't + * exist. + */ +void snd_ctl_sync_vmaster_hook(struct snd_kcontrol *kcontrol) +{ + struct link_master *master; + if (!kcontrol) + return; + master = snd_kcontrol_chip(kcontrol); + if (master->hook) + master->hook(master->hook_private_data, master->val); +} +EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster_hook); diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index cd094ecaca3b..d428ffede4f3 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -611,7 +611,6 @@ static void isight_card_free(struct snd_card *card) fw_iso_resources_destroy(&isight->resources); fw_unit_put(isight->unit); - fw_device_put(isight->device); mutex_destroy(&isight->mutex); } @@ -644,7 +643,7 @@ static int isight_probe(struct device *unit_dev) isight->card = card; mutex_init(&isight->mutex); isight->unit = fw_unit_get(unit); - isight->device = fw_device_get(fw_dev); + isight->device = fw_dev; isight->audio_base = get_unit_base(unit); if (!isight->audio_base) { dev_err(&unit->device, "audio unit base not found\n"); @@ -681,7 +680,6 @@ static int isight_probe(struct device *unit_dev) err_unit: fw_unit_put(isight->unit); - fw_device_put(isight->device); mutex_destroy(&isight->mutex); error: snd_card_free(card); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index cbe6bb9e53b6..297244e658d9 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -656,12 +656,10 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit) static void fwspk_card_free(struct snd_card *card) { struct fwspk *fwspk = card->private_data; - struct fw_device *dev = fw_parent_device(fwspk->unit); amdtp_out_stream_destroy(&fwspk->stream); cmp_connection_destroy(&fwspk->connection); fw_unit_put(fwspk->unit); - fw_device_put(dev); mutex_destroy(&fwspk->mutex); } @@ -718,7 +716,6 @@ static int __devinit fwspk_probe(struct device *unit_dev) fwspk = card->private_data; fwspk->card = card; mutex_init(&fwspk->mutex); - fw_device_get(fw_dev); fwspk->unit = fw_unit_get(unit); fwspk->device_info = fwspk_detect(fw_dev); if (!fwspk->device_info) { @@ -767,7 +764,6 @@ err_connection: cmp_connection_destroy(&fwspk->connection); err_unit: fw_unit_put(fwspk->unit); - fw_device_put(fw_dev); mutex_destroy(&fwspk->mutex); error: snd_card_free(card); diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 6b68c8206805..a63faec5e7fd 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -25,21 +25,20 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/version.h> +#include <linux/sched.h> +#include <media/v4l2-device.h> #include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <sound/tea575x-tuner.h> MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); MODULE_LICENSE("GPL"); -static int radio_nr = -1; -module_param(radio_nr, int, 0); - -#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) -#define FREQ_LO (50UL * 16000) -#define FREQ_HI (150UL * 16000) +#define FREQ_LO (76U * 16000) +#define FREQ_HI (108U * 16000) /* * definitions @@ -90,7 +89,7 @@ static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) tea->ops->set_pins(tea, 0); } -static unsigned int snd_tea575x_read(struct snd_tea575x *tea) +static u32 snd_tea575x_read(struct snd_tea575x *tea) { u16 l, rdata; u32 data = 0; @@ -121,11 +120,13 @@ static unsigned int snd_tea575x_read(struct snd_tea575x *tea) return data; } -static void snd_tea575x_get_freq(struct snd_tea575x *tea) +static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) { - unsigned long freq; + u32 freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK; + + if (freq == 0) + return freq; - freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK; /* freq *= 12.5 */ freq *= 125; freq /= 10; @@ -135,14 +136,13 @@ static void snd_tea575x_get_freq(struct snd_tea575x *tea) else freq -= TEA575X_FMIF; - tea->freq = freq * 16; /* from kHz */ + return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */ } static void snd_tea575x_set_freq(struct snd_tea575x *tea) { - unsigned long freq; + u32 freq = tea->freq; - freq = clamp(tea->freq, FREQ_LO, FREQ_HI); freq /= 16; /* to kHz */ /* crystal fixup */ if (tea->tea5759) @@ -167,12 +167,14 @@ static int vidioc_querycap(struct file *file, void *priv, { struct snd_tea575x *tea = video_drvdata(file); - strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); + strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); strlcpy(v->card, tea->card, sizeof(v->card)); strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + if (!tea->cannot_read_data) + v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -191,18 +193,24 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; v->rangelow = FREQ_LO; v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + v->audmode = (tea->val & TEA575X_BIT_MONO) ? + V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; v->signal = tea->tuned ? 0xffff : 0; - return 0; } static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - if (v->index > 0) + struct snd_tea575x *tea = video_drvdata(file); + + if (v->index) return -EINVAL; + tea->val &= ~TEA575X_BIT_MONO; + if (v->audmode == V4L2_TUNER_MODE_MONO) + tea->val |= TEA575X_BIT_MONO; + snd_tea575x_write(tea, tea->val); return 0; } @@ -214,7 +222,6 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; f->type = V4L2_TUNER_RADIO; - snd_tea575x_get_freq(tea); f->frequency = tea->freq; return 0; } @@ -227,33 +234,72 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; - - tea->freq = f->frequency; - + tea->val &= ~TEA575X_BIT_SEARCH; + tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI); snd_tea575x_set_freq(tea); - return 0; } -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, + struct v4l2_hw_freq_seek *a) { - if (a->index > 1) - return -EINVAL; - - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} + struct snd_tea575x *tea = video_drvdata(file); + unsigned long timeout; + int i; -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index != 0) + if (tea->cannot_read_data) + return -ENOTTY; + if (a->tuner || a->wrap_around) return -EINVAL; - return 0; + + /* clear the frequency, HW will fill it in */ + tea->val &= ~TEA575X_BIT_FREQ_MASK; + tea->val |= TEA575X_BIT_SEARCH; + if (a->seek_upward) + tea->val |= TEA575X_BIT_UPDOWN; + else + tea->val &= ~TEA575X_BIT_UPDOWN; + snd_tea575x_write(tea, tea->val); + timeout = jiffies + msecs_to_jiffies(10000); + for (;;) { + if (time_after(jiffies, timeout)) + break; + if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { + /* some signal arrived, stop search */ + tea->val &= ~TEA575X_BIT_SEARCH; + snd_tea575x_set_freq(tea); + return -ERESTARTSYS; + } + if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) { + u32 freq; + + /* Found a frequency, wait until it can be read */ + for (i = 0; i < 100; i++) { + msleep(10); + freq = snd_tea575x_get_freq(tea); + if (freq) /* available */ + break; + } + if (freq == 0) /* shouldn't happen */ + break; + /* + * if we moved by less than 50 kHz, or in the wrong + * direction, continue seeking + */ + if (abs(tea->freq - freq) < 16 * 50 || + (a->seek_upward && freq < tea->freq) || + (!a->seek_upward && freq > tea->freq)) { + snd_tea575x_write(tea, tea->val); + continue; + } + tea->freq = freq; + tea->val &= ~TEA575X_BIT_SEARCH; + return 0; + } + } + tea->val &= ~TEA575X_BIT_SEARCH; + snd_tea575x_set_freq(tea); + return -EAGAIN; } static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -273,23 +319,27 @@ static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) static const struct v4l2_file_operations tea575x_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, }; static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static struct video_device tea575x_radio = { - .name = "tea575x-tuner", +static const struct video_device tea575x_radio = { .fops = &tea575x_fops, .ioctl_ops = &tea575x_ioctl_ops, - .release = video_device_release_empty, + .release = video_device_release_empty, }; static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { @@ -303,27 +353,34 @@ int snd_tea575x_init(struct snd_tea575x *tea) { int retval; - tea->mute = 1; + tea->mute = true; - snd_tea575x_write(tea, 0x55AA); - if (snd_tea575x_read(tea) != 0x55AA) - return -ENODEV; + /* Not all devices can or know how to read the data back. + Such devices can set cannot_read_data to true. */ + if (!tea->cannot_read_data) { + snd_tea575x_write(tea, 0x55AA); + if (snd_tea575x_read(tea) != 0x55AA) + return -ENODEV; + } - tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; + tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea); tea->vd = tea575x_radio; video_set_drvdata(&tea->vd, tea); mutex_init(&tea->mutex); + strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); tea->vd.lock = &tea->mutex; + tea->vd.v4l2_dev = tea->v4l2_dev; + tea->vd.ctrl_handler = &tea->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); - tea->vd.ctrl_handler = &tea->ctrl_handler; v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); retval = tea->ctrl_handler.error; if (retval) { - printk(KERN_ERR "tea575x-tuner: can't initialize controls\n"); + v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); v4l2_ctrl_handler_free(&tea->ctrl_handler); return retval; } @@ -338,9 +395,9 @@ int snd_tea575x_init(struct snd_tea575x *tea) v4l2_ctrl_handler_setup(&tea->ctrl_handler); - retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr); + retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr); if (retval) { - printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); + v4l2_err(tea->v4l2_dev, "can't register video device!\n"); v4l2_ctrl_handler_free(&tea->ctrl_handler); return retval; } diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index bb938153a964..466a5c8e8354 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -26,7 +26,7 @@ #include <sound/mpu401.h> #include <sound/hwdep.h> #include <sound/ac97_codec.h> - +#include <sound/tlv.h> #endif #ifndef CHIP_AU8820 @@ -107,6 +107,14 @@ #define NR_WTPB 0x20 /* WT channels per each bank. */ #define NR_PCM 0x10 +struct pcm_vol { + struct snd_kcontrol *kctl; + int active; + int dma; + int mixin[4]; + int vol[4]; +}; + /* Structs */ typedef struct { //int this_08; /* Still unknown */ @@ -168,6 +176,7 @@ struct snd_vortex { /* Xtalk canceler */ int xt_mode; /* 1: speakers, 0:headphones. */ #endif + struct pcm_vol pcm_vol[NR_PCM]; int isquad; /* cache of extended ID codec flag. */ @@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt); /* Connection stuff. */ static void vortex_connect_default(vortex_t * vortex, int en); static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, - int dir, int type); + int dir, int type, int subdev); static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype); #ifndef CHIP_AU8810 diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 6933a27a5d76..525f881f0409 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) } /* Default Connections */ -static int -vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type); static void vortex_connect_default(vortex_t * vortex, int en) { @@ -2111,15 +2109,13 @@ static void vortex_connect_default(vortex_t * vortex, int en) Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0. */ static int -vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) +vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, + int type, int subdev) { stream_t *stream; int i, en; + struct pcm_vol *p; - if ((nr_ch == 3) - || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2))) - return -EBUSY; - if (dma >= 0) { en = 0; vortex_adb_checkinout(vortex, @@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) MIX_DEFIGAIN); #endif } + if (stream->type == VORTEX_PCM_ADB && en) { + p = &vortex->pcm_vol[subdev]; + p->dma = dma; + for (i = 0; i < nr_ch; i++) + p->mixin[i] = mix[i]; + for (i = 0; i < ch_top; i++) + p->vol[i] = 0; + } } #ifndef CHIP_AU8820 else { @@ -2473,7 +2477,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) hwread(vortex->mmio, VORTEX_IRQ_STAT); handled = 1; } - if (source & IRQ_MIDI) { + if ((source & IRQ_MIDI) && vortex->rmidi) { snd_mpu401_uart_interrupt(vortex->irq, vortex->rmidi->private_data); handled = 1; diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 0ef2f9712208..e59f120742a4 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { .mask = 0, }; #endif + +static void vortex_notify_pcm_vol_change(struct snd_card *card, + struct snd_kcontrol *kctl, int activate) +{ + if (activate) + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id)); +} + /* open callback */ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) { @@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, if (stream != NULL) vortex_adb_allocroute(chip, stream->dma, stream->nr_ch, stream->dir, - stream->type); + stream->type, + substream->number); /* Alloc routes. */ dma = vortex_adb_allocroute(chip, -1, params_channels(hw_params), - substream->stream, type); + substream->stream, type, + substream->number); if (dma < 0) { spin_unlock_irq(&chip->lock); return dma; @@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, vortex_adbdma_setbuffers(chip, dma, params_period_bytes(hw_params), params_periods(hw_params)); + if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { + chip->pcm_vol[substream->number].active = 1; + vortex_notify_pcm_vol_change(chip->card, + chip->pcm_vol[substream->number].kctl, 1); + } } #ifndef CHIP_AU8810 else { @@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream) spin_lock_irq(&chip->lock); // Delete audio routes. if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { - if (stream != NULL) + if (stream != NULL) { + if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { + chip->pcm_vol[substream->number].active = 0; + vortex_notify_pcm_vol_change(chip->card, + chip->pcm_vol[substream->number].kctl, + 0); + } vortex_adb_allocroute(chip, stream->dma, stream->nr_ch, stream->dir, - stream->type); + stream->type, + substream->number); + } } #ifndef CHIP_AU8810 else { @@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { }, }; +/* subdevice PCM Volume control */ + +static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + vortex_t *vortex = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2); + uinfo->value.integer.min = -128; + uinfo->value.integer.max = 32; + return 0; +} + +static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + vortex_t *vortex = snd_kcontrol_chip(kcontrol); + int subdev = kcontrol->id.subdevice; + struct pcm_vol *p = &vortex->pcm_vol[subdev]; + int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); + for (i = 0; i < max_chn; i++) + ucontrol->value.integer.value[i] = p->vol[i]; + return 0; +} + +static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + int changed = 0; + int mixin; + unsigned char vol; + vortex_t *vortex = snd_kcontrol_chip(kcontrol); + int subdev = kcontrol->id.subdevice; + struct pcm_vol *p = &vortex->pcm_vol[subdev]; + int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); + for (i = 0; i < max_chn; i++) { + if (p->vol[i] != ucontrol->value.integer.value[i]) { + p->vol[i] = ucontrol->value.integer.value[i]; + if (p->active) { + switch (vortex->dma_adb[p->dma].nr_ch) { + case 1: + mixin = p->mixin[0]; + break; + case 2: + default: + mixin = p->mixin[(i < 2) ? i : (i - 2)]; + break; + case 4: + mixin = p->mixin[i]; + break; + }; + vol = p->vol[i]; + vortex_mix_setinputvolumebyte(vortex, + vortex->mixplayb[i], mixin, vol); + } + changed = 1; + } + } + return changed; +} + +static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400); + +static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_vortex_pcm_vol_info, + .get = snd_vortex_pcm_vol_get, + .put = snd_vortex_pcm_vol_put, + .tlv = { .p = vortex_pcm_vol_db_scale }, +}; + /* create a pcm device */ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) { @@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) return err; } } + if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) { + for (i = 0; i < NR_PCM; i++) { + chip->pcm_vol[i].active = 0; + chip->pcm_vol[i].dma = -1; + kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip); + if (!kctl) + return -ENOMEM; + chip->pcm_vol[i].kctl = kctl; + kctl->id.device = 0; + kctl->id.subdevice = i; + err = snd_ctl_add(chip->card, kctl); + if (err < 0) + return err; + } + } return 0; } diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index b78f3fc3c33c..6109490b83e8 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -36,7 +36,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size) size = CT_PAGE_ALIGN(size); if (size > vm->size) { - printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " + printk(KERN_ERR "ctxfi: Fail! No sufficient device virtual " "memory space available!\n"); return NULL; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index cb557c603a80..a8faae1c85e4 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -142,6 +142,7 @@ static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; #ifdef SUPPORT_JOYSTICK static bool joystick[SNDRV_CARDS]; #endif +static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); @@ -165,6 +166,9 @@ MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)"); module_param_array(joystick, bool, NULL, 0444); MODULE_PARM_DESC(joystick, "Enable joystick."); #endif +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); + #define NR_APUS 64 @@ -558,6 +562,7 @@ struct es1968 { struct work_struct hwvol_work; #ifdef CONFIG_SND_ES1968_RADIO + struct v4l2_device v4l2_dev; struct snd_tea575x tea; #endif }; @@ -2613,6 +2618,7 @@ static int snd_es1968_free(struct es1968 *chip) #ifdef CONFIG_SND_ES1968_RADIO snd_tea575x_exit(&chip->tea); + v4l2_device_unregister(&chip->v4l2_dev); #endif if (chip->irq >= 0) @@ -2655,6 +2661,7 @@ static int __devinit snd_es1968_create(struct snd_card *card, int capt_streams, int chip_type, int do_pm, + int radio_nr, struct es1968 **chip_ret) { static struct snd_device_ops ops = { @@ -2751,7 +2758,14 @@ static int __devinit snd_es1968_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef CONFIG_SND_ES1968_RADIO + err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); + if (err < 0) { + snd_es1968_free(chip); + return err; + } + chip->tea.v4l2_dev = &chip->v4l2_dev; chip->tea.private_data = chip; + chip->tea.radio_nr = radio_nr; chip->tea.ops = &snd_es1968_tea_ops; strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card)); sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); @@ -2797,6 +2811,7 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci, pcm_substreams_c[dev], pci_id->driver_data, use_pm[dev], + radio_nr[dev], &chip)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 9597ef1eccca..a416ea8af3e9 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -58,6 +58,7 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * High 16-bits are video (radio) device number + 1 */ static int tea575x_tuner[SNDRV_CARDS]; +static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); @@ -67,6 +68,9 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); module_param_array(tea575x_tuner, int, NULL, 0444); MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only)."); +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); + #define TUNER_DISABLED (1<<3) #define TUNER_ONLY (1<<4) @@ -197,6 +201,7 @@ struct fm801 { struct snd_info_entry *proc_entry; #ifdef CONFIG_SND_FM801_TEA575X_BOOL + struct v4l2_device v4l2_dev; struct snd_tea575x tea; #endif @@ -1154,8 +1159,10 @@ static int snd_fm801_free(struct fm801 *chip) __end_hw: #ifdef CONFIG_SND_FM801_TEA575X_BOOL - if (!(chip->tea575x_tuner & TUNER_DISABLED)) + if (!(chip->tea575x_tuner & TUNER_DISABLED)) { snd_tea575x_exit(&chip->tea); + v4l2_device_unregister(&chip->v4l2_dev); + } #endif if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -1175,6 +1182,7 @@ static int snd_fm801_dev_free(struct snd_device *device) static int __devinit snd_fm801_create(struct snd_card *card, struct pci_dev * pci, int tea575x_tuner, + int radio_nr, struct fm801 ** rchip) { struct fm801 *chip; @@ -1234,6 +1242,13 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef CONFIG_SND_FM801_TEA575X_BOOL + err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); + if (err < 0) { + snd_fm801_free(chip); + return err; + } + chip->tea.v4l2_dev = &chip->v4l2_dev; + chip->tea.radio_nr = radio_nr; chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); @@ -1241,6 +1256,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, (tea575x_tuner & TUNER_TYPE_MASK) < 4) { if (snd_tea575x_init(&chip->tea)) { snd_printk(KERN_ERR "TEA575x radio not found\n"); + snd_fm801_free(chip); return -ENODEV; } } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0) { @@ -1287,7 +1303,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci, err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) return err; - if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) { + if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/hda/alc260_quirks.c b/sound/pci/hda/alc260_quirks.c deleted file mode 100644 index 3b5170b9700f..000000000000 --- a/sound/pci/hda/alc260_quirks.c +++ /dev/null @@ -1,968 +0,0 @@ -/* - * ALC260 quirk models - * included by patch_realtek.c - */ - -/* ALC260 models */ -enum { - ALC260_AUTO, - ALC260_BASIC, - ALC260_FUJITSU_S702X, - ALC260_ACER, - ALC260_WILL, - ALC260_REPLACER_672V, - ALC260_FAVORIT100, -#ifdef CONFIG_SND_DEBUG - ALC260_TEST, -#endif - ALC260_MODEL_LAST /* last tag */ -}; - -static const hda_nid_t alc260_dac_nids[1] = { - /* front */ - 0x02, -}; - -static const hda_nid_t alc260_adc_nids[1] = { - /* ADC0 */ - 0x04, -}; - -static const hda_nid_t alc260_adc_nids_alt[1] = { - /* ADC1 */ - 0x05, -}; - -/* NIDs used when simultaneous access to both ADCs makes sense. Note that - * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. - */ -static const hda_nid_t alc260_dual_adc_nids[2] = { - /* ADC0, ADC1 */ - 0x04, 0x05 -}; - -#define ALC260_DIGOUT_NID 0x03 -#define ALC260_DIGIN_NID 0x06 - -static const struct hda_input_mux alc260_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack, - * headphone jack and the internal CD lines since these are the only pins at - * which audio can appear. For flexibility, also allow the option of - * recording the mixer output on the second ADC (ADC0 doesn't have a - * connection to the mixer output). - */ -static const struct hda_input_mux alc260_fujitsu_capture_sources[2] = { - { - .num_items = 3, - .items = { - { "Mic/Line", 0x0 }, - { "CD", 0x4 }, - { "Headphone", 0x2 }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic/Line", 0x0 }, - { "CD", 0x4 }, - { "Headphone", 0x2 }, - { "Mixer", 0x5 }, - }, - }, - -}; - -/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to - * the Fujitsu S702x, but jacks are marked differently. - */ -static const struct hda_input_mux alc260_acer_capture_sources[2] = { - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Headphone", 0x5 }, - }, - }, - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Headphone", 0x6 }, - { "Mixer", 0x5 }, - }, - }, -}; - -/* Maxdata Favorit 100XS */ -static const struct hda_input_mux alc260_favorit100_capture_sources[2] = { - { - .num_items = 2, - .items = { - { "Line/Mic", 0x0 }, - { "CD", 0x4 }, - }, - }, - { - .num_items = 3, - .items = { - { "Line/Mic", 0x0 }, - { "CD", 0x4 }, - { "Mixer", 0x5 }, - }, - }, -}; - -/* - * This is just place-holder, so there's something for alc_build_pcms to look - * at when it calculates the maximum number of channels. ALC260 has no mixer - * element which allows changing the channel mode, so the verb list is - * never used. - */ -static const struct hda_channel_mode alc260_modes[1] = { - { 2, NULL }, -}; - - -/* Mixer combinations - * - * basic: base_output + input + pc_beep + capture - * fujitsu: fujitsu + capture - * acer: acer + capture - */ - -static const struct snd_kcontrol_new alc260_base_output_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc260_input_mixer[] = { - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT), - { } /* end */ -}; - -/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, - * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. - */ -static const struct snd_kcontrol_new alc260_fujitsu_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT), - { } /* end */ -}; - -/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current - * versions of the ALC260 don't act on requests to enable mic bias from NID - * 0x0f (used to drive the headphone jack in these laptops). The ALC260 - * datasheet doesn't mention this restriction. At this stage it's not clear - * whether this behaviour is intentional or is a hardware bug in chip - * revisions available in early 2006. Therefore for now allow the - * "Headphone Jack Mode" control to span all choices, but if it turns out - * that the lack of mic bias for this NID is intentional we could change the - * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. - * - * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006 - * don't appear to make the mic bias available from the "line" jack, even - * though the NID used for this jack (0x14) can supply it. The theory is - * that perhaps Acer have included blocking capacitors between the ALC260 - * and the output jack. If this turns out to be the case for all such - * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT - * to ALC_PIN_DIR_INOUT_NOMICBIAS. - * - * The C20x Tablet series have a mono internal speaker which is controlled - * via the chip's Mono sum widget and pin complex, so include the necessary - * controls for such models. On models without a "mono speaker" the control - * won't do anything. - */ -static const struct snd_kcontrol_new alc260_acer_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, - HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - { } /* end */ -}; - -/* Maxdata Favorit 100XS: one output and one input (0x12) jack - */ -static const struct snd_kcontrol_new alc260_favorit100_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), - ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - { } /* end */ -}; - -/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12, - * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17. - */ -static const struct snd_kcontrol_new alc260_will_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - { } /* end */ -}; - -/* Replacer 672V ALC260 pin usage: Mic jack = 0x12, - * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f. - */ -static const struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), - ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), - ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb alc260_init_verbs[] = { - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* LINE-2 is used for line-out in rear */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* select line-out */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LINE-OUT pin */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* enable HP */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* enable Mono */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* mute capture amp left and right */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* set connection select to line in (default select for this ADC) */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* set vol=0 Line-Out mixer amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* set vol=0 HP mixer amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* set vol=0 Mono mixer amp left and right */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* unmute pin widget amp left and right (no gain on this amp) */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* unmute LINE-2 out pin */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & - * Line In 2 = 0x03 - */ - /* mute analog inputs */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ - /* mute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* mute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* mute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -/* Initialisation sequence for ALC260 as configured in Fujitsu S702x - * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD - * audio = 0x16, internal speaker = 0x10. - */ -static const struct hda_verb alc260_fujitsu_init_verbs[] = { - /* Disable all GPIOs */ - {0x01, AC_VERB_SET_GPIO_MASK, 0}, - /* Internal speaker is connected to headphone pin */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Headphone/Line-out jack connects to Line1 pin; make it an output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mic/Line-in jack is connected to mic1 pin, so make it an input */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Ensure all other unused pins are disabled and muted. */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Line1 pin widget takes its input from the OUT1 sum bus - * when acting as an output. - */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Line1 pin widget output buffer since it starts as an output. - * If the pin mode is changed by the user the pin mode control will - * take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute input buffer of pin widget used for Line-in (no equiv - * mixer ctrl) - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - line - * in (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do the same for the second ADC: mute capture input amp and - * set ADC connection to line in (on mic1 pin) - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -/* Initialisation sequence for ALC260 as configured in Acer TravelMate and - * similar laptops (adapted from Fujitsu init verbs). - */ -static const struct hda_verb alc260_acer_init_verbs[] = { - /* On TravelMate laptops, GPIO 0 enables the internal speaker and - * the headphone jack. Turn this on and rely on the standard mute - * methods whenever the user wants to turn these outputs off. - */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - /* Internal speaker/Headphone jack is connected to Line-out pin */ - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Internal microphone/Mic jack is connected to Mic1 pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - /* Line In jack is connected to Line1 pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Ensure all other unused pins are disabled and muted. */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum - * bus when acting as outputs. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute Line-out pin widget amp left and right - * (no equiv mixer ctrl) - */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute mono pin widget amp output (no equiv mixer ctrl) */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mic1 and Line1 pin widget input buffers since they start as - * inputs. If the pin mode is changed by the user the pin mode control - * will take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - mic - * (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do similar with the second ADC: mute capture input amp and - * set ADC connection to mic to match ALSA's default state. - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -/* Initialisation sequence for Maxdata Favorit 100XS - * (adapted from Acer init verbs). - */ -static const struct hda_verb alc260_favorit100_init_verbs[] = { - /* GPIO 0 enables the output jack. - * Turn this on and rely on the standard mute - * methods whenever the user wants to turn these outputs off. - */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - /* Line/Mic input jack is connected to Mic1 pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - /* Ensure all other unused pins are disabled and muted. */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Disable digital (SPDIF) pins */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum - * bus when acting as outputs. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute Line-out pin widget amp left and right - * (no equiv mixer ctrl) - */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mic1 and Line1 pin widget input buffers since they start as - * inputs. If the pin mode is changed by the user the pin mode control - * will take care of enabling the pin's input/output buffers as needed. - * Therefore there's no need to enable the input buffer at this - * stage. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting - mic - * (on mic1 pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do similar with the second ADC: mute capture input amp and - * set ADC connection to mic to match ALSA's default state. - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; - -static const struct hda_verb alc260_will_verbs[] = { - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x1a, AC_VERB_SET_PROC_COEF, 0x3040}, - {} -}; - -static const struct hda_verb alc260_replacer_672v_verbs[] = { - {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x1a, AC_VERB_SET_PROC_COEF, 0x3050}, - - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - - {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {} -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc260_replacer_672v_automute(struct hda_codec *codec) -{ - unsigned int present; - - /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ - present = snd_hda_jack_detect(codec, 0x0f); - if (present) { - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 1); - snd_hda_codec_write_cache(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_HP); - } else { - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write_cache(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - } -} - -static void alc260_replacer_672v_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC_HP_EVENT) - alc260_replacer_672v_automute(codec); -} - -static const struct hda_verb alc260_hp_dc7600_verbs[] = { - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {} -}; - -/* Test configuration for debugging, modelled after the ALC880 test - * configuration. - */ -#ifdef CONFIG_SND_DEBUG -static const hda_nid_t alc260_test_dac_nids[1] = { - 0x02, -}; -static const hda_nid_t alc260_test_adc_nids[2] = { - 0x04, 0x05, -}; -/* For testing the ALC260, each input MUX needs its own definition since - * the signal assignments are different. This assumes that the first ADC - * is NID 0x04. - */ -static const struct hda_input_mux alc260_test_capture_sources[2] = { - { - .num_items = 7, - .items = { - { "MIC1 pin", 0x0 }, - { "MIC2 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "LINE2 pin", 0x3 }, - { "CD pin", 0x4 }, - { "LINE-OUT pin", 0x5 }, - { "HP-OUT pin", 0x6 }, - }, - }, - { - .num_items = 8, - .items = { - { "MIC1 pin", 0x0 }, - { "MIC2 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "LINE2 pin", 0x3 }, - { "CD pin", 0x4 }, - { "Mixer", 0x5 }, - { "LINE-OUT pin", 0x6 }, - { "HP-OUT pin", 0x7 }, - }, - }, -}; -static const struct snd_kcontrol_new alc260_test_mixer[] = { - /* Output driver widgets */ - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT), - HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT), - - /* Modes for retasking pin widgets - * Note: the ALC260 doesn't seem to act on requests to enable mic - * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't - * mention this restriction. At this stage it's not clear whether - * this behaviour is intentional or is a hardware bug in chip - * revisions available at least up until early 2006. Therefore for - * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all - * choices, but if it turns out that the lack of mic bias for these - * NIDs is intentional we could change their modes from - * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS. - */ - ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT), - - /* Loopback mixer controls */ - HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT), - HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT), - - /* Controls for GPIO pins, assuming they are configured as outputs */ - ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), - ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), - ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), - ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), - - /* Switches to allow the digital IO pins to be enabled. The datasheet - * is ambigious as to which NID is which; testing on laptops which - * make this output available should provide clarification. - */ - ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), - ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), - - /* A switch allowing EAPD to be enabled. Some laptops seem to use - * this output to turn on an external amplifier. - */ - ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), - ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), - - { } /* end */ -}; -static const struct hda_verb alc260_test_init_verbs[] = { - /* Enable all GPIOs as outputs with an initial value of 0 */ - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - {0x01, AC_VERB_SET_GPIO_MASK, 0x0f}, - - /* Enable retasking pins as output, initially without power amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* Disable digital (SPDIF) pins initially, but users can enable - * them via a mixer switch. In the case of SPDIF-out, this initverb - * payload also sets the generation to 0, output to be in "consumer" - * PCM format, copyright asserted, no pre-emphasis and no validity - * control. - */ - {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, - {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the - * OUT1 sum bus when acting as an output. - */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0c, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0}, - - /* Start with output sum widgets muted and their output gains at min */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Unmute retasking pin widget output buffers since the default - * state appears to be output. As the pin mode is changed by the - * user the pin mode control will take care of enabling the pin's - * input/output buffers as needed. - */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Also unmute the mono-out pin widget */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mute capture amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - /* Set ADC connection select to match default mixer setting (mic1 - * pin) - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Do the same for the second ADC: mute capture input amp and - * set ADC connection to mic1 pin - */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ - - { } -}; -#endif - -/* - * ALC260 configurations - */ -static const char * const alc260_models[ALC260_MODEL_LAST] = { - [ALC260_BASIC] = "basic", - [ALC260_FUJITSU_S702X] = "fujitsu", - [ALC260_ACER] = "acer", - [ALC260_WILL] = "will", - [ALC260_REPLACER_672V] = "replacer", - [ALC260_FAVORIT100] = "favorit100", -#ifdef CONFIG_SND_DEBUG - [ALC260_TEST] = "test", -#endif - [ALC260_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc260_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100), - SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), - SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), - SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), - {} -}; - -static const struct alc_config_preset alc260_presets[] = { - [ALC260_BASIC] = { - .mixers = { alc260_base_output_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - }, - [ALC260_FUJITSU_S702X] = { - .mixers = { alc260_fujitsu_mixer }, - .init_verbs = { alc260_fujitsu_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources), - .input_mux = alc260_fujitsu_capture_sources, - }, - [ALC260_ACER] = { - .mixers = { alc260_acer_mixer }, - .init_verbs = { alc260_acer_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), - .input_mux = alc260_acer_capture_sources, - }, - [ALC260_FAVORIT100] = { - .mixers = { alc260_favorit100_mixer }, - .init_verbs = { alc260_favorit100_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_dual_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources), - .input_mux = alc260_favorit100_capture_sources, - }, - [ALC260_WILL] = { - .mixers = { alc260_will_mixer }, - .init_verbs = { alc260_init_verbs, alc260_will_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), - .adc_nids = alc260_adc_nids, - .dig_out_nid = ALC260_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - }, - [ALC260_REPLACER_672V] = { - .mixers = { alc260_replacer_672v_mixer }, - .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), - .adc_nids = alc260_adc_nids, - .dig_out_nid = ALC260_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc260_replacer_672v_unsol_event, - .init_hook = alc260_replacer_672v_automute, - }, -#ifdef CONFIG_SND_DEBUG - [ALC260_TEST] = { - .mixers = { alc260_test_mixer }, - .init_verbs = { alc260_test_init_verbs }, - .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), - .dac_nids = alc260_test_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids), - .adc_nids = alc260_test_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources), - .input_mux = alc260_test_capture_sources, - }, -#endif -}; - diff --git a/sound/pci/hda/alc880_quirks.c b/sound/pci/hda/alc880_quirks.c deleted file mode 100644 index 501501ef36a9..000000000000 --- a/sound/pci/hda/alc880_quirks.c +++ /dev/null @@ -1,1707 +0,0 @@ -/* - * ALC880 quirk models - * included by patch_realtek.c - */ - -/* ALC880 board config type */ -enum { - ALC880_AUTO, - ALC880_3ST, - ALC880_3ST_DIG, - ALC880_5ST, - ALC880_5ST_DIG, - ALC880_W810, - ALC880_Z71V, - ALC880_6ST, - ALC880_6ST_DIG, - ALC880_F1734, - ALC880_ASUS, - ALC880_ASUS_DIG, - ALC880_ASUS_W1V, - ALC880_ASUS_DIG2, - ALC880_FUJITSU, - ALC880_UNIWILL_DIG, - ALC880_UNIWILL, - ALC880_UNIWILL_P53, - ALC880_CLEVO, - ALC880_TCL_S700, - ALC880_LG, -#ifdef CONFIG_SND_DEBUG - ALC880_TEST, -#endif - ALC880_MODEL_LAST /* last tag */ -}; - -/* - * ALC880 3-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) - * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, - * F-Mic = 0x1b, HP = 0x19 - */ - -static const hda_nid_t alc880_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x05, 0x04, 0x03 -}; - -static const hda_nid_t alc880_adc_nids[3] = { - /* ADC0-2 */ - 0x07, 0x08, 0x09, -}; - -/* The datasheet says the node 0x07 is connected from inputs, - * but it shows zero connection in the real implementation on some devices. - * Note: this is a 915GAV bug, fixed on 915GLV - */ -static const hda_nid_t alc880_adc_nids_alt[2] = { - /* ADC1-2 */ - 0x08, 0x09, -}; - -#define ALC880_DIGOUT_NID 0x06 -#define ALC880_DIGIN_NID 0x0a -#define ALC880_PIN_CD_NID 0x1c - -static const struct hda_input_mux alc880_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x3 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* channel source setting (2/6 channel selection for 3-stack) */ -/* 2ch mode */ -static const struct hda_verb alc880_threestack_ch2_init[] = { - /* set line-in to input, mute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - /* set mic-in to input vref 80%, mute it */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* 6ch mode */ -static const struct hda_verb alc880_threestack_ch6_init[] = { - /* set line-in to output, unmute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - /* set mic-in to output, unmute it */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode alc880_threestack_modes[2] = { - { 2, alc880_threestack_ch2_init }, - { 6, alc880_threestack_ch6_init }, -}; - -static const struct snd_kcontrol_new alc880_three_stack_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* - * ALC880 5-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), - * Side = 0x02 (0xd) - * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 - * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 - */ - -/* additional mixers to alc880_three_stack_mixer */ -static const struct snd_kcontrol_new alc880_five_stack_mixer[] = { - HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT), - { } /* end */ -}; - -/* channel source setting (6/8 channel selection for 5-stack) */ -/* 6ch mode */ -static const struct hda_verb alc880_fivestack_ch6_init[] = { - /* set line-in to input, mute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* 8ch mode */ -static const struct hda_verb alc880_fivestack_ch8_init[] = { - /* set line-in to output, unmute it */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode alc880_fivestack_modes[2] = { - { 6, alc880_fivestack_ch6_init }, - { 8, alc880_fivestack_ch8_init }, -}; - - -/* - * ALC880 6-stack model - * - * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), - * Side = 0x05 (0x0f) - * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, - * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b - */ - -static const hda_nid_t alc880_6st_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x03, 0x04, 0x05 -}; - -static const struct hda_input_mux alc880_6stack_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* fixed 8-channels */ -static const struct hda_channel_mode alc880_sixstack_modes[1] = { - { 8, NULL }, -}; - -static const struct snd_kcontrol_new alc880_six_stack_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - - -/* - * ALC880 W810 model - * - * W810 has rear IO for: - * Front (DAC 02) - * Surround (DAC 03) - * Center/LFE (DAC 04) - * Digital out (06) - * - * The system also has a pair of internal speakers, and a headphone jack. - * These are both connected to Line2 on the codec, hence to DAC 02. - * - * There is a variable resistor to control the speaker or headphone - * volume. This is a hardware-only device without a software API. - * - * Plugging headphones in will disable the internal speakers. This is - * implemented in hardware, not via the driver using jack sense. In - * a similar fashion, plugging into the rear socket marked "front" will - * disable both the speakers and headphones. - * - * For input, there's a microphone jack, and an "audio in" jack. - * These may not do anything useful with this driver yet, because I - * haven't setup any initialization verbs for these yet... - */ - -static const hda_nid_t alc880_w810_dac_nids[3] = { - /* front, rear/surround, clfe */ - 0x02, 0x03, 0x04 -}; - -/* fixed 6 channels */ -static const struct hda_channel_mode alc880_w810_modes[1] = { - { 6, NULL } -}; - -/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */ -static const struct snd_kcontrol_new alc880_w810_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - { } /* end */ -}; - - -/* - * Z710V model - * - * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) - * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), - * Line = 0x1a - */ - -static const hda_nid_t alc880_z71v_dac_nids[1] = { - 0x02 -}; -#define ALC880_Z71V_HP_DAC 0x03 - -/* fixed 2 channels */ -static const struct hda_channel_mode alc880_2_jack_modes[1] = { - { 2, NULL } -}; - -static const struct snd_kcontrol_new alc880_z71v_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - - -/* - * ALC880 F1734 model - * - * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d) - * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18 - */ - -static const hda_nid_t alc880_f1734_dac_nids[1] = { - 0x03 -}; -#define ALC880_F1734_HP_DAC 0x02 - -static const struct snd_kcontrol_new alc880_f1734_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_input_mux alc880_f1734_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "CD", 0x4 }, - }, -}; - - -/* - * ALC880 ASUS model - * - * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) - * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, - * Mic = 0x18, Line = 0x1a - */ - -#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */ -#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */ - -static const struct snd_kcontrol_new alc880_asus_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -/* - * ALC880 ASUS W1V model - * - * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e) - * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16, - * Mic = 0x18, Line = 0x1a, Line2 = 0x1b - */ - -/* additional mixers to alc880_asus_mixer */ -static const struct snd_kcontrol_new alc880_asus_w1v_mixer[] = { - HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT), - { } /* end */ -}; - -/* TCL S700 */ -static const struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* Uniwill */ -static const struct snd_kcontrol_new alc880_uniwill_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc880_fujitsu_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* - * initialize the codec volumes, etc - */ - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc880_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for front - * panel mic (mic 2) - */ - /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - - /* - * Set up output mixers (0x0c - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - { } -}; - -/* - * 3-stack pin configuration: - * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b - */ -static const struct hda_verb alc880_pin_3stack_init_verbs[] = { - /* - * preset connection lists of input pins - * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround - */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ - - /* - * Set pin mode and muting - */ - /* set front pin widgets 0x14 for output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mic2 (as headphone out) for HP output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line2 (as front mic) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * 5-stack pin configuration: - * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19, - * line-in/side = 0x1a, f-mic = 0x1b - */ -static const struct hda_verb alc880_pin_5stack_init_verbs[] = { - /* - * preset connection lists of input pins - * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround - */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */ - - /* - * Set pin mode and muting - */ - /* set pin widgets 0x14-0x17 for output */ - {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_OUT}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* unmute pins for output (no gain on this amp) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mic2 (as headphone out) for HP output */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line2 (as front mic) pin widget for input and vref at 80% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * W810 pin configuration: - * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b - */ -static const struct hda_verb alc880_pin_w810_init_verbs[] = { - /* hphone/speaker input selector: front DAC */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - { } -}; - -/* - * Z71V pin configuration: - * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?) - */ -static const struct hda_verb alc880_pin_z71v_init_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * 6-stack pin configuration: - * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, - * f-mic = 0x19, line = 0x1a, HP = 0x1b - */ -static const struct hda_verb alc880_pin_6stack_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* - * Uniwill pin configuration: - * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19, - * line = 0x1a - */ -static const struct hda_verb alc880_uniwill_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */ - /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, - - { } -}; - -/* -* Uniwill P53 -* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, - */ -static const struct hda_verb alc880_uniwill_p53_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_DCVOL_EVENT}, - - { } -}; - -static const struct hda_verb alc880_beep_init_verbs[] = { - { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, - { } -}; - -static void alc880_uniwill_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x16; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -static void alc880_uniwill_init_hook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); -} - -static void alc880_uniwill_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - res >>= 28; - switch (res) { - case ALC_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_exec_unsol_event(codec, res); - break; - } -} - -static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) -{ - alc_exec_unsol_event(codec, res >> 28); -} - -static void alc880_uniwill_p53_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - present &= HDA_AMP_VOLMASK; - snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, - HDA_AMP_VOLMASK, present); - snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0, - HDA_AMP_VOLMASK, present); -} - -static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - res >>= 28; - if (res == ALC_DCVOL_EVENT) - alc880_uniwill_p53_dcvol_automute(codec); - else - alc_exec_unsol_event(codec, res); -} - -/* - * F1734 pin configuration: - * HP = 0x14, speaker-out = 0x15, mic = 0x18 - */ -static const struct hda_verb alc880_pin_f1734_init_verbs[] = { - {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_DCVOL_EVENT}, - - { } -}; - -/* - * ASUS pin configuration: - * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a - */ -static const struct hda_verb alc880_pin_asus_init_verbs[] = { - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - { } -}; - -/* Enable GPIO mask and set output */ -#define alc880_gpio1_init_verbs alc_gpio1_init_verbs -#define alc880_gpio2_init_verbs alc_gpio2_init_verbs -#define alc880_gpio3_init_verbs alc_gpio3_init_verbs - -/* Clevo m520g init */ -static const struct hda_verb alc880_pin_clevo_init_verbs[] = { - /* headphone output */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* line-out */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Line-in */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* CD */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic1 (rear panel) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Mic2 (front panel) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* headphone */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - { } -}; - -static const struct hda_verb alc880_pin_tcl_S700_init_verbs[] = { - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - /* Headphone output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Front output*/ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Line In pin widget for input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - - { } -}; - -/* - * LG m1 express dual - * - * Pin assignment: - * Rear Line-In/Out (blue): 0x14 - * Build-in Mic-In: 0x15 - * Speaker-out: 0x17 - * HP-Out (green): 0x1b - * Mic-In/Out (red): 0x19 - * SPDIF-Out: 0x1e - */ - -/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */ -static const hda_nid_t alc880_lg_dac_nids[3] = { - 0x05, 0x02, 0x03 -}; - -/* seems analog CD is not working */ -static const struct hda_input_mux alc880_lg_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x1 }, - { "Line", 0x5 }, - { "Internal Mic", 0x6 }, - }, -}; - -/* 2,4,6 channel modes */ -static const struct hda_verb alc880_lg_ch2_init[] = { - /* set line-in and mic-in to input */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } -}; - -static const struct hda_verb alc880_lg_ch4_init[] = { - /* set line-in to out and mic-in to input */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } -}; - -static const struct hda_verb alc880_lg_ch6_init[] = { - /* set line-in and mic-in to output */ - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { } -}; - -static const struct hda_channel_mode alc880_lg_ch_modes[3] = { - { 2, alc880_lg_ch2_init }, - { 4, alc880_lg_ch4_init }, - { 6, alc880_lg_ch6_init }, -}; - -static const struct snd_kcontrol_new alc880_lg_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc880_lg_init_verbs[] = { - /* set capture source to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* mute all amp mixer inputs */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* line-in to input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* built-in mic */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* speaker-out */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* mic-in to input */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* HP-out */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x17; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc880_lg_loopbacks[] = { - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 6 }, - { 0x0b, HDA_INPUT, 7 }, - { } /* end */ -}; -#endif - -/* - * Test configuration for debugging - * - * Almost all inputs/outputs are enabled. I/O pins can be configured via - * enum controls. - */ -#ifdef CONFIG_SND_DEBUG -static const hda_nid_t alc880_test_dac_nids[4] = { - 0x02, 0x03, 0x04, 0x05 -}; - -static const struct hda_input_mux alc880_test_capture_source = { - .num_items = 7, - .items = { - { "In-1", 0x0 }, - { "In-2", 0x1 }, - { "In-3", 0x2 }, - { "In-4", 0x3 }, - { "CD", 0x4 }, - { "Front", 0x5 }, - { "Surround", 0x6 }, - }, -}; - -static const struct hda_channel_mode alc880_test_modes[4] = { - { 2, NULL }, - { 4, NULL }, - { 6, NULL }, - { 8, NULL }, -}; - -static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "N/A", "Line Out", "HP Out", - "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item >= 8) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int pin_ctl, item = 0; - - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (pin_ctl & AC_PINCTL_OUT_EN) { - if (pin_ctl & AC_PINCTL_HP_EN) - item = 2; - else - item = 1; - } else if (pin_ctl & AC_PINCTL_IN_EN) { - switch (pin_ctl & AC_PINCTL_VREFEN) { - case AC_PINCTL_VREF_HIZ: item = 3; break; - case AC_PINCTL_VREF_50: item = 4; break; - case AC_PINCTL_VREF_GRD: item = 5; break; - case AC_PINCTL_VREF_80: item = 6; break; - case AC_PINCTL_VREF_100: item = 7; break; - } - } - ucontrol->value.enumerated.item[0] = item; - return 0; -} - -static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - static const unsigned int ctls[] = { - 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_50, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_80, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_100, - }; - unsigned int old_ctl, new_ctl; - - old_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - new_ctl = ctls[ucontrol->value.enumerated.item[0]]; - if (old_ctl != new_ctl) { - int val; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - new_ctl); - val = ucontrol->value.enumerated.item[0] >= 3 ? - HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, val); - return 1; - } - return 0; -} - -static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "Front", "Surround", "CLFE", "Side" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int sel; - - sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); - ucontrol->value.enumerated.item[0] = sel & 3; - return 0; -} - -static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = (hda_nid_t)kcontrol->private_value; - unsigned int sel; - - sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; - if (ucontrol->value.enumerated.item[0] != sel) { - sel = ucontrol->value.enumerated.item[0] & 3; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, sel); - return 1; - } - return 0; -} - -#define PIN_CTL_TEST(xname,nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_test_pin_ctl_info, \ - .get = alc_test_pin_ctl_get, \ - .put = alc_test_pin_ctl_put, \ - .private_value = nid \ - } - -#define PIN_SRC_TEST(xname,nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_test_pin_src_info, \ - .get = alc_test_pin_src_get, \ - .put = alc_test_pin_src_put, \ - .private_value = nid \ - } - -static const struct snd_kcontrol_new alc880_test_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - PIN_CTL_TEST("Front Pin Mode", 0x14), - PIN_CTL_TEST("Surround Pin Mode", 0x15), - PIN_CTL_TEST("CLFE Pin Mode", 0x16), - PIN_CTL_TEST("Side Pin Mode", 0x17), - PIN_CTL_TEST("In-1 Pin Mode", 0x18), - PIN_CTL_TEST("In-2 Pin Mode", 0x19), - PIN_CTL_TEST("In-3 Pin Mode", 0x1a), - PIN_CTL_TEST("In-4 Pin Mode", 0x1b), - PIN_SRC_TEST("In-1 Pin Source", 0x18), - PIN_SRC_TEST("In-2 Pin Source", 0x19), - PIN_SRC_TEST("In-3 Pin Source", 0x1a), - PIN_SRC_TEST("In-4 Pin Source", 0x1b), - HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc880_test_init_verbs[] = { - /* Unmute inputs of 0x0c - 0x0f */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Vol output for 0x0c-0x0f */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set output pins 0x14-0x17 */ - {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_OUT}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Unmute output pins 0x14-0x17 */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Set input pins 0x18-0x1c */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Mute input pins 0x18-0x1b */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* ADC set up */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Analog input/passthru */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - { } -}; -#endif - -/* - */ - -static const char * const alc880_models[ALC880_MODEL_LAST] = { - [ALC880_3ST] = "3stack", - [ALC880_TCL_S700] = "tcl", - [ALC880_3ST_DIG] = "3stack-digout", - [ALC880_CLEVO] = "clevo", - [ALC880_5ST] = "5stack", - [ALC880_5ST_DIG] = "5stack-digout", - [ALC880_W810] = "w810", - [ALC880_Z71V] = "z71v", - [ALC880_6ST] = "6stack", - [ALC880_6ST_DIG] = "6stack-digout", - [ALC880_ASUS] = "asus", - [ALC880_ASUS_W1V] = "asus-w1v", - [ALC880_ASUS_DIG] = "asus-dig", - [ALC880_ASUS_DIG2] = "asus-dig2", - [ALC880_UNIWILL_DIG] = "uniwill", - [ALC880_UNIWILL_P53] = "uniwill-p53", - [ALC880_FUJITSU] = "fujitsu", - [ALC880_F1734] = "F1734", - [ALC880_LG] = "lg", -#ifdef CONFIG_SND_DEBUG - [ALC880_TEST] = "test", -#endif - [ALC880_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc880_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), - SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V), - /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ - SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), - SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), - SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */ - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), - SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), - {} -}; - -/* - * ALC880 codec presets - */ -static const struct alc_config_preset alc880_presets[] = { - [ALC880_3ST] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_3ST_DIG] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_TCL_S700] = { - .mixers = { alc880_tcl_s700_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_tcl_S700_init_verbs, - alc880_gpio2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ - .num_adc_nids = 1, /* single ADC */ - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_5ST] = { - .mixers = { alc880_three_stack_mixer, - alc880_five_stack_mixer}, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_5stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), - .channel_mode = alc880_fivestack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_5ST_DIG] = { - .mixers = { alc880_three_stack_mixer, - alc880_five_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_5stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes), - .channel_mode = alc880_fivestack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_6ST] = { - .mixers = { alc880_six_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), - .dac_nids = alc880_6st_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), - .channel_mode = alc880_sixstack_modes, - .input_mux = &alc880_6stack_capture_source, - }, - [ALC880_6ST_DIG] = { - .mixers = { alc880_six_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), - .dac_nids = alc880_6st_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), - .channel_mode = alc880_sixstack_modes, - .input_mux = &alc880_6stack_capture_source, - }, - [ALC880_W810] = { - .mixers = { alc880_w810_base_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_w810_init_verbs, - alc880_gpio2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), - .dac_nids = alc880_w810_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), - .channel_mode = alc880_w810_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_Z71V] = { - .mixers = { alc880_z71v_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_z71v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), - .dac_nids = alc880_z71v_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - }, - [ALC880_F1734] = { - .mixers = { alc880_f1734_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_f1734_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids), - .dac_nids = alc880_f1734_dac_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_f1734_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_ASUS] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_DIG] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_DIG2] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio2_init_verbs }, /* use GPIO2 */ - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_ASUS_W1V] = { - .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_UNIWILL_DIG] = { - .mixers = { alc880_asus_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_asus_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_asus_modes), - .channel_mode = alc880_asus_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_UNIWILL] = { - .mixers = { alc880_uniwill_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_unsol_event, - .setup = alc880_uniwill_setup, - .init_hook = alc880_uniwill_init_hook, - }, - [ALC880_UNIWILL_P53] = { - .mixers = { alc880_uniwill_p53_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_p53_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), - .dac_nids = alc880_asus_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_w810_modes), - .channel_mode = alc880_threestack_modes, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_FUJITSU] = { - .mixers = { alc880_fujitsu_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_uniwill_p53_init_verbs, - alc880_beep_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, - .unsol_event = alc880_uniwill_p53_unsol_event, - .setup = alc880_uniwill_p53_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_CLEVO] = { - .mixers = { alc880_three_stack_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_pin_clevo_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_dac_nids), - .dac_nids = alc880_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc880_capture_source, - }, - [ALC880_LG] = { - .mixers = { alc880_lg_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_lg_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids), - .dac_nids = alc880_lg_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes), - .channel_mode = alc880_lg_ch_modes, - .need_dac_fix = 1, - .input_mux = &alc880_lg_capture_source, - .unsol_event = alc880_unsol_event, - .setup = alc880_lg_setup, - .init_hook = alc_hp_automute, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .loopbacks = alc880_lg_loopbacks, -#endif - }, -#ifdef CONFIG_SND_DEBUG - [ALC880_TEST] = { - .mixers = { alc880_test_mixer }, - .init_verbs = { alc880_test_init_verbs }, - .num_dacs = ARRAY_SIZE(alc880_test_dac_nids), - .dac_nids = alc880_test_dac_nids, - .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_test_modes), - .channel_mode = alc880_test_modes, - .input_mux = &alc880_test_capture_source, - }, -#endif -}; - diff --git a/sound/pci/hda/alc882_quirks.c b/sound/pci/hda/alc882_quirks.c deleted file mode 100644 index bb364a53f546..000000000000 --- a/sound/pci/hda/alc882_quirks.c +++ /dev/null @@ -1,866 +0,0 @@ -/* - * ALC882/ALC883/ALC888/ALC889 quirk models - * included by patch_realtek.c - */ - -/* ALC882 models */ -enum { - ALC882_AUTO, - ALC885_MBA21, - ALC885_MBP3, - ALC885_MB5, - ALC885_MACMINI3, - ALC885_IMAC91, - ALC889A_MB31, - ALC882_MODEL_LAST, -}; - -#define ALC882_DIGOUT_NID 0x06 -#define ALC882_DIGIN_NID 0x0a -#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID -#define ALC883_DIGIN_NID ALC882_DIGIN_NID -#define ALC1200_DIGOUT_NID 0x10 - - -static const struct hda_channel_mode alc882_ch_modes[1] = { - { 8, NULL } -}; - -/* DACs */ -static const hda_nid_t alc882_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x03, 0x04, 0x05 -}; -#define alc883_dac_nids alc882_dac_nids - -/* ADCs */ -#define alc882_adc_nids alc880_adc_nids -#define alc882_adc_nids_alt alc880_adc_nids_alt -#define alc883_adc_nids alc882_adc_nids_alt - -static const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; -#define alc883_capsrc_nids alc882_capsrc_nids_alt - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ - -static const struct hda_input_mux alc882_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -#define alc883_capture_source alc882_capture_source - -static const struct hda_input_mux mb5_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x1 }, - { "Line", 0x7 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux macmini3_capture_source = { - .num_items = 2, - .items = { - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_3stack_6ch_intel = { - .num_items = 4, - .items = { - { "Mic", 0x1 }, - { "Front Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc889A_mb31_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - /* Front Mic (0x01) unused */ - { "Line", 0x2 }, - /* Line 2 (0x03) unused */ - /* CD (0x04) unused? */ - }, -}; - -static const struct hda_input_mux alc889A_imac91_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x01 }, - { "Line", 0x2 }, /* Not sure! */ - }, -}; - -/* Macbook Air 2,1 */ - -static const struct hda_channel_mode alc885_mba21_ch_modes[1] = { - { 2, NULL }, -}; - -/* - * macbook pro ALC885 can switch LineIn to LineOut without losing Mic - */ - -/* - * 2ch mode - */ -static const struct hda_verb alc885_mbp_ch2_init[] = { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc885_mbp_ch4_init[] = { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } /* end */ -}; - -static const struct hda_channel_mode alc885_mbp_4ch_modes[2] = { - { 2, alc885_mbp_ch2_init }, - { 4, alc885_mbp_ch4_init }, -}; - -/* - * 2ch - * Speakers/Woofer/HP = Front - * LineIn = Input - */ -static const struct hda_verb alc885_mb5_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } /* end */ -}; - -/* - * 6ch mode - * Speakers/HP = Front - * Woofer = LFE - * LineIn = Surround - */ -static const struct hda_verb alc885_mb5_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - { } /* end */ -}; - -static const struct hda_channel_mode alc885_mb5_6ch_modes[2] = { - { 2, alc885_mb5_ch2_init }, - { 6, alc885_mb5_ch6_init }, -}; - -#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes - -/* Macbook Air 2,1 same control for HP and internal Speaker */ - -static const struct snd_kcontrol_new alc885_mba21_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT), - { } -}; - - -static const struct snd_kcontrol_new alc885_mbp3_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_mb5_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_macmini3_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x00, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_imac91_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), - { } /* end */ -}; - - -static const struct snd_kcontrol_new alc882_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc882_base_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin: output 3 (0x0f) */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - { } -}; - -#define alc883_init_verbs alc882_base_init_verbs - -/* Macbook 5,1 */ -static const struct hda_verb alc885_mb5_init_verbs[] = { - /* DACs */ - {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}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Surround mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* LFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LFE Pin (0x0e) */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* HP Pin (0x0f) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)}, - { } -}; - -/* Macmini 3,1 */ -static const struct hda_verb alc885_macmini3_init_verbs[] = { - /* DACs */ - {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}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Surround mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* LFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* LFE Pin (0x0e) */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* HP Pin (0x0f) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, - /* Line In pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - { } -}; - - -static const struct hda_verb alc885_mba21_init_verbs[] = { - /*Internal and HP Speaker Mixer*/ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /*Internal Speaker Pin (0x0c)*/ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: output 0 (0x0e) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC_HP_EVENT | AC_USRSP_EN)}, - /* Line in (is hp when jack connected)*/ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - { } - }; - - -/* Macbook Pro rev3 */ -static const struct hda_verb alc885_mbp3_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* HP mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: output 0 (0x0e) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: use output 1 when in LineOut mode */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC2: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - { } -}; - -/* iMac 9,1 */ -static const struct hda_verb alc885_imac91_init_verbs[] = { - /* Internal Speaker Pin (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP Pin: Rear */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC_HP_EVENT | AC_USRSP_EN)}, - /* Line in Rear */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } -}; - -/* Toggle speaker-output according to the hp-jack state */ -static void alc885_imac24_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - spec->autocfg.speaker_pins[1] = 0x1a; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -#define alc885_mb5_setup alc885_imac24_setup -#define alc885_macmini3_setup alc885_imac24_setup - -/* Macbook Air 2,1 */ -static void alc885_mba21_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - - - -static void alc885_mbp3_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -static void alc885_imac91_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x18; - spec->autocfg.speaker_pins[1] = 0x1a; - alc_simple_setup_automute(spec, ALC_AUTOMUTE_AMP); -} - -/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ -static const struct hda_verb alc889A_mb31_ch2_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ - { } /* end */ -}; - -/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */ -static const struct hda_verb alc889A_mb31_ch4_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ - { } /* end */ -}; - -/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */ -static const struct hda_verb alc889A_mb31_ch5_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ - { } /* end */ -}; - -/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */ -static const struct hda_verb alc889A_mb31_ch6_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ - { } /* end */ -}; - -static const struct hda_channel_mode alc889A_mb31_6ch_modes[4] = { - { 2, alc889A_mb31_ch2_init }, - { 4, alc889A_mb31_ch4_init }, - { 5, alc889A_mb31_ch5_init }, - { 6, alc889A_mb31_ch6_init }, -}; - -static const struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc889A_mb31_mixer[] = { - /* Output mixers */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT), - /* Output switches */ - HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT), - /* Boost mixers */ - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x1a, 0x00, HDA_INPUT), - /* Input mixers */ - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_chmode_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_ch_mode_info, - .get = alc_ch_mode_get, - .put = alc_ch_mode_put, - }, - { } /* end */ -}; - -static const struct hda_verb alc889A_mb31_verbs[] = { - /* Init rear pin (used as headphone output) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, - /* Init line pin (used as output in 4ch and 6ch mode) */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */ - /* Init line 2 pin (used as headphone out by default) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */ - { } /* end */ -}; - -/* Mute speakers according to the headphone jack state */ -static void alc889A_mb31_automute(struct hda_codec *codec) -{ - unsigned int present; - - /* Mute only in 2ch or 4ch mode */ - if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) - == 0x00) { - present = snd_hda_jack_detect(codec, 0x15); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - } -} - -static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC_HP_EVENT) - alc889A_mb31_automute(codec); -} - -static void alc882_unsol_event(struct hda_codec *codec, unsigned int res) -{ - alc_exec_unsol_event(codec, res >> 26); -} - -/* - * configuration and preset - */ -static const char * const alc882_models[ALC882_MODEL_LAST] = { - [ALC885_MB5] = "mb5", - [ALC885_MACMINI3] = "macmini3", - [ALC885_MBA21] = "mba21", - [ALC885_MBP3] = "mbp3", - [ALC885_IMAC91] = "imac91", - [ALC889A_MB31] = "mb31", - [ALC882_AUTO] = "auto", -}; - -/* codec SSID table for Intel Mac */ -static const struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5), - /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2, - * so apparently no perfect solution yet - */ - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3), - {} /* terminator */ -}; - -static const struct alc_config_preset alc882_presets[] = { - [ALC885_MBA21] = { - .mixers = { alc885_mba21_mixer }, - .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs }, - .num_dacs = 2, - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mba21_ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), - .input_mux = &alc882_capture_source, - .unsol_event = alc882_unsol_event, - .setup = alc885_mba21_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MBP3] = { - .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mbp3_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = 2, - .dac_nids = alc882_dac_nids, - .hp_nid = 0x04, - .channel_mode = alc885_mbp_4ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc882_unsol_event, - .setup = alc885_mbp3_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MB5] = { - .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mb5_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mb5_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), - .input_mux = &mb5_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc882_unsol_event, - .setup = alc885_mb5_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MACMINI3] = { - .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_macmini3_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_macmini3_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes), - .input_mux = &macmini3_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc882_unsol_event, - .setup = alc885_macmini3_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_IMAC91] = { - .mixers = {alc885_imac91_mixer}, - .init_verbs = { alc885_imac91_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mba21_ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), - .input_mux = &alc889A_imac91_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc882_unsol_event, - .setup = alc885_imac91_setup, - .init_hook = alc_hp_automute, - }, - [ALC889A_MB31] = { - .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer}, - .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs, - alc880_gpio1_init_verbs }, - .adc_nids = alc883_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .capsrc_nids = alc883_capsrc_nids, - .dac_nids = alc883_dac_nids, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .channel_mode = alc889A_mb31_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes), - .input_mux = &alc889A_mb31_capture_source, - .dig_out_nid = ALC883_DIGOUT_NID, - .unsol_event = alc889A_mb31_unsol_event, - .init_hook = alc889A_mb31_automute, - }, -}; - - diff --git a/sound/pci/hda/alc_quirks.c b/sound/pci/hda/alc_quirks.c deleted file mode 100644 index a18952ed4311..000000000000 --- a/sound/pci/hda/alc_quirks.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Common codes for Realtek codec quirks - * included by patch_realtek.c - */ - -/* - * configuration template - to be copied to the spec instance - */ -struct alc_config_preset { - const struct snd_kcontrol_new *mixers[5]; /* should be identical size - * with spec - */ - const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ - const struct hda_verb *init_verbs[5]; - unsigned int num_dacs; - const hda_nid_t *dac_nids; - hda_nid_t dig_out_nid; /* optional */ - hda_nid_t hp_nid; /* optional */ - const hda_nid_t *slave_dig_outs; - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - const hda_nid_t *capsrc_nids; - hda_nid_t dig_in_nid; - unsigned int num_channel_mode; - const struct hda_channel_mode *channel_mode; - int need_dac_fix; - int const_channel_count; - unsigned int num_mux_defs; - const struct hda_input_mux *input_mux; - void (*unsol_event)(struct hda_codec *, unsigned int); - void (*setup)(struct hda_codec *); - void (*init_hook)(struct hda_codec *); -#ifdef CONFIG_SND_HDA_POWER_SAVE - const struct hda_amp_list *loopbacks; - void (*power_hook)(struct hda_codec *codec); -#endif -}; - -/* - * channel mode setting - */ -static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - spec->ext_channel_count); -} - -static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->ext_channel_count); - if (err >= 0 && !spec->const_channel_count) { - spec->multiout.max_channels = spec->ext_channel_count; - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - } - return err; -} - -/* - * Control the mode of pin widget settings via the mixer. "pc" is used - * instead of "%" to avoid consequences of accidentally treating the % as - * being part of a format specifier. Maximum allowed length of a value is - * 63 characters plus NULL terminator. - * - * Note: some retasking pin complexes seem to ignore requests for input - * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these - * are requested. Therefore order this list so that this behaviour will not - * cause problems when mixer clients move through the enum sequentially. - * NIDs 0x0f and 0x10 have been observed to have this behaviour as of - * March 2006. - */ -static const char * const alc_pin_mode_names[] = { - "Mic 50pc bias", "Mic 80pc bias", - "Line in", "Line out", "Headphone out", -}; -static const unsigned char alc_pin_mode_values[] = { - PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP, -}; -/* The control can present all 5 options, or it can limit the options based - * in the pin being assumed to be exclusively an input or an output pin. In - * addition, "input" pins may or may not process the mic bias option - * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to - * accept requests for bias as of chip versions up to March 2006) and/or - * wiring in the computer. - */ -#define ALC_PIN_DIR_IN 0x00 -#define ALC_PIN_DIR_OUT 0x01 -#define ALC_PIN_DIR_INOUT 0x02 -#define ALC_PIN_DIR_IN_NOMICBIAS 0x03 -#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04 - -/* Info about the pin modes supported by the different pin direction modes. - * For each direction the minimum and maximum values are given. - */ -static const signed char alc_pin_mode_dir_info[5][2] = { - { 0, 2 }, /* ALC_PIN_DIR_IN */ - { 3, 4 }, /* ALC_PIN_DIR_OUT */ - { 0, 4 }, /* ALC_PIN_DIR_INOUT */ - { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */ - { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */ -}; -#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0]) -#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1]) -#define alc_pin_mode_n_items(_dir) \ - (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) - -static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int item_num = uinfo->value.enumerated.item; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = alc_pin_mode_n_items(dir); - - if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir)) - item_num = alc_pin_mode_min(dir); - strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]); - return 0; -} - -static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int i; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - - /* Find enumerated value for current pinctl setting */ - i = alc_pin_mode_min(dir); - while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl) - i++; - *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); - return 0; -} - -static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char dir = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - - if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) - val = alc_pin_mode_min(dir); - - change = pinctl != alc_pin_mode_values[val]; - if (change) { - /* Set pin mode to that requested */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - alc_pin_mode_values[val]); - - /* Also enable the retasking pin's input/output as required - * for the requested pin mode. Enum values of 2 or less are - * input modes. - * - * Dynamically switching the input/output buffers probably - * reduces noise slightly (particularly on input) so we'll - * do it. However, having both input and output buffers - * enabled simultaneously doesn't seem to be problematic if - * this turns out to be necessary in the future. - */ - if (val <= 2) { - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, 0); - } else { - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - } - } - return change; -} - -#define ALC_PIN_MODE(xname, nid, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_pin_mode_info, \ - .get = alc_pin_mode_get, \ - .put = alc_pin_mode_put, \ - .private_value = nid | (dir<<16) } - -/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged - * together using a mask with more than one bit set. This control is - * currently used only by the ALC260 test model. At this stage they are not - * needed for any "production" models. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_gpio_data_info snd_ctl_boolean_mono_info - -static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0x00); - - *valp = (val & mask) != 0; - return 0; -} -static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, - 0x00); - - /* Set/unset the masked GPIO bit(s) as needed */ - change = (val == 0 ? 0 : mask) != (gpio_data & mask); - if (val == 0) - gpio_data &= ~mask; - else - gpio_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_GPIO_DATA, gpio_data); - - return change; -} -#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_gpio_data_info, \ - .get = alc_gpio_data_get, \ - .put = alc_gpio_data_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - -/* A switch control to allow the enabling of the digital IO pins on the - * ALC260. This is incredibly simplistic; the intention of this control is - * to provide something in the test model allowing digital outputs to be - * identified if present. If models are found which can utilise these - * outputs a more complete mixer control can be devised for those models if - * necessary. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info - -static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0x00); - - *valp = (val & mask) != 0; - return 0; -} -static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - signed int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, - 0x00); - - /* Set/unset the masked control bit(s) as needed */ - change = (val == 0 ? 0 : mask) != (ctrl_data & mask); - if (val==0) - ctrl_data &= ~mask; - else - ctrl_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - ctrl_data); - - return change; -} -#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_spdif_ctrl_info, \ - .get = alc_spdif_ctrl_get, \ - .put = alc_spdif_ctrl_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - -/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. - * Again, this is only used in the ALC26x test models to help identify when - * the EAPD line must be asserted for features to work. - */ -#ifdef CONFIG_SND_DEBUG -#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info - -static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0x00); - - *valp = (val & mask) != 0; - return 0; -} - -static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int change; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value & 0xffff; - unsigned char mask = (kcontrol->private_value >> 16) & 0xff; - long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, - 0x00); - - /* Set/unset the masked control bit(s) as needed */ - change = (!val ? 0 : mask) != (ctrl_data & mask); - if (!val) - ctrl_data &= ~mask; - else - ctrl_data |= mask; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - ctrl_data); - - return change; -} - -#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ - .info = alc_eapd_ctrl_info, \ - .get = alc_eapd_ctrl_get, \ - .put = alc_eapd_ctrl_put, \ - .private_value = nid | (mask<<16) } -#endif /* CONFIG_SND_DEBUG */ - -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++; - } -} - -/* - * set up from the preset table - */ -static void setup_preset(struct hda_codec *codec, - const struct alc_config_preset *preset) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) - add_mixer(spec, preset->mixers[i]); - spec->cap_mixer = preset->cap_mixer; - for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; - i++) - add_verb(spec, preset->init_verbs[i]); - - spec->channel_mode = preset->channel_mode; - spec->num_channel_mode = preset->num_channel_mode; - spec->need_dac_fix = preset->need_dac_fix; - spec->const_channel_count = preset->const_channel_count; - - if (preset->const_channel_count) - spec->multiout.max_channels = preset->const_channel_count; - else - spec->multiout.max_channels = spec->channel_mode[0].channels; - spec->ext_channel_count = spec->channel_mode[0].channels; - - spec->multiout.num_dacs = preset->num_dacs; - spec->multiout.dac_nids = preset->dac_nids; - spec->multiout.dig_out_nid = preset->dig_out_nid; - spec->multiout.slave_dig_outs = preset->slave_dig_outs; - spec->multiout.hp_nid = preset->hp_nid; - - spec->num_mux_defs = preset->num_mux_defs; - if (!spec->num_mux_defs) - spec->num_mux_defs = 1; - spec->input_mux = preset->input_mux; - - spec->num_adc_nids = preset->num_adc_nids; - spec->adc_nids = preset->adc_nids; - spec->capsrc_nids = preset->capsrc_nids; - spec->dig_in_nid = preset->dig_in_nid; - - spec->unsol_event = preset->unsol_event; - spec->init_hook = preset->init_hook; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->power_hook = preset->power_hook; - spec->loopback.amplist = preset->loopbacks; -#endif - - if (preset->setup) - preset->setup(codec); - - alc_fixup_autocfg_pin_nums(codec); -} - -static void alc_simple_setup_automute(struct alc_spec *spec, int mode) -{ - int lo_pin = spec->autocfg.line_out_pins[0]; - - if (lo_pin == spec->autocfg.speaker_pins[0] || - lo_pin == spec->autocfg.hp_pins[0]) - lo_pin = 0; - spec->automute_mode = mode; - spec->detect_hp = !!spec->autocfg.hp_pins[0]; - spec->detect_lo = !!lo_pin; - spec->automute_lo = spec->automute_lo_possible = !!lo_pin; - spec->automute_speaker = spec->automute_speaker_possible = !!spec->autocfg.speaker_pins[0]; -} - -/* auto-toggle front mic */ -static void alc88x_simple_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_jack_detect(codec, 0x18); - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); -} - diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 684307372d73..7a8fcc4c15f8 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -2304,7 +2305,7 @@ typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *); /* apply the function to all matching slave ctls in the mixer list */ static int map_slaves(struct hda_codec *codec, const char * const *slaves, - map_slave_func_t func, void *data) + const char *suffix, map_slave_func_t func, void *data) { struct hda_nid_item *items; const char * const *s; @@ -2317,7 +2318,14 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves, sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) continue; for (s = slaves; *s; s++) { - if (!strcmp(sctl->id.name, *s)) { + char tmpname[sizeof(sctl->id.name)]; + const char *name = *s; + if (suffix) { + snprintf(tmpname, sizeof(tmpname), "%s %s", + name, suffix); + name = tmpname; + } + if (!strcmp(sctl->id.name, name)) { err = func(data, sctl); if (err) return err; @@ -2333,12 +2341,65 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) return 1; } +/* guess the value corresponding to 0dB */ +static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) +{ + int _tlv[4]; + const int *tlv = NULL; + int val = -1; + + if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + /* FIXME: set_fs() hack for obtaining user-space TLV data */ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv)) + tlv = _tlv; + set_fs(fs); + } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) + tlv = kctl->tlv.p; + if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) + val = -tlv[2] / tlv[3]; + return val; +} + +/* call kctl->put with the given value(s) */ +static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) +{ + struct snd_ctl_elem_value *ucontrol; + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return -ENOMEM; + ucontrol->value.integer.value[0] = val; + ucontrol->value.integer.value[1] = val; + kctl->put(kctl, ucontrol); + kfree(ucontrol); + return 0; +} + +/* initialize the slave volume with 0dB */ +static int init_slave_0dB(void *data, struct snd_kcontrol *slave) +{ + int offset = get_kctl_0dB_offset(slave); + if (offset > 0) + put_kctl_with_value(slave, offset); + return 0; +} + +/* unmute the slave */ +static int init_slave_unmute(void *data, struct snd_kcontrol *slave) +{ + return put_kctl_with_value(slave, 1); +} + /** * snd_hda_add_vmaster - create a virtual master control and add slaves * @codec: HD-audio codec * @name: vmaster control name * @tlv: TLV data (optional) * @slaves: slave control names (optional) + * @suffix: suffix string to each slave name (optional) + * @init_slave_vol: initialize slaves to unmute/0dB + * @ctl_ret: store the vmaster kcontrol in return * * Create a virtual master control with the given name. The TLV data * must be either NULL or a valid data. @@ -2349,13 +2410,18 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) * * This function returns zero if successful or a negative error code. */ -int snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *slaves) +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char * const *slaves, + const char *suffix, bool init_slave_vol, + struct snd_kcontrol **ctl_ret) { struct snd_kcontrol *kctl; int err; - err = map_slaves(codec, slaves, check_slave_present, NULL); + if (ctl_ret) + *ctl_ret = NULL; + + err = map_slaves(codec, slaves, suffix, check_slave_present, NULL); if (err != 1) { snd_printdd("No slave found for %s\n", name); return 0; @@ -2367,13 +2433,119 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, if (err < 0) return err; - err = map_slaves(codec, slaves, (map_slave_func_t)snd_ctl_add_slave, - kctl); + err = map_slaves(codec, slaves, suffix, + (map_slave_func_t)snd_ctl_add_slave, kctl); if (err < 0) return err; + + /* init with master mute & zero volume */ + put_kctl_with_value(kctl, 0); + if (init_slave_vol) + map_slaves(codec, slaves, suffix, + tlv ? init_slave_0dB : init_slave_unmute, kctl); + + if (ctl_ret) + *ctl_ret = kctl; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); +EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster); + +/* + * mute-LED control using vmaster + */ +static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "Off", "On", "Follow Master" + }; + unsigned int index; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + index = uinfo->value.enumerated.item; + if (index >= 3) + index = 2; + strcpy(uinfo->value.enumerated.name, texts[index]); + return 0; +} + +static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = hook->mute_mode; + return 0; +} + +static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); + unsigned int old_mode = hook->mute_mode; + + hook->mute_mode = ucontrol->value.enumerated.item[0]; + if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER) + hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; + if (old_mode == hook->mute_mode) + return 0; + snd_hda_sync_vmaster_hook(hook); + return 1; +} + +static struct snd_kcontrol_new vmaster_mute_mode = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mute-LED Mode", + .info = vmaster_mute_mode_info, + .get = vmaster_mute_mode_get, + .put = vmaster_mute_mode_put, +}; + +/* + * Add a mute-LED hook with the given vmaster switch kctl + * "Mute-LED Mode" control is automatically created and associated with + * the given hook. + */ +int snd_hda_add_vmaster_hook(struct hda_codec *codec, + struct hda_vmaster_mute_hook *hook, + bool expose_enum_ctl) +{ + struct snd_kcontrol *kctl; + + if (!hook->hook || !hook->sw_kctl) + return 0; + snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec); + hook->codec = codec; + hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; + if (!expose_enum_ctl) + return 0; + kctl = snd_ctl_new1(&vmaster_mute_mode, hook); + if (!kctl) + return -ENOMEM; + return snd_hda_ctl_add(codec, 0, kctl); +} +EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook); + +/* + * Call the hook with the current value for synchronization + * Should be called in init callback + */ +void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) +{ + if (!hook->hook || !hook->codec) + return; + switch (hook->mute_mode) { + case HDA_VMUTE_FOLLOW_MASTER: + snd_ctl_sync_vmaster_hook(hook->sw_kctl); + break; + default: + hook->hook(hook->codec, hook->mute_mode); + break; + } +} +EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook); + /** * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch @@ -5272,6 +5444,10 @@ int snd_hda_suspend(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (hda_codec_is_power_on(codec)) hda_call_codec_suspend(codec); + else /* forcibly change the power to D3 even if not used */ + hda_set_power_state(codec, + codec->afg ? codec->afg : codec->mfg, + AC_PWRST_D3); if (codec->patch_ops.post_suspend) codec->patch_ops.post_suspend(codec); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f0f1943a4b2c..9a9f372e1be4 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -855,6 +855,7 @@ struct hda_codec { unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */ + unsigned int no_jack_detect:1; /* Machine has no jack-detection */ #ifdef CONFIG_SND_HDA_POWER_SAVE unsigned int power_on :1; /* current (global) power-state */ unsigned int power_transition :1; /* power-state in transition */ diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index c1da422e085a..b58b4b1687fa 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -385,8 +385,8 @@ error: static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) { static unsigned int alsa_rates[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, - 96000, 176400, 192000, 384000 + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000 }; int i, j; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 95dfb6874941..c19e71a94e1b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -84,7 +84,7 @@ 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, "DMA pointer read method." - "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO)."); + "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); 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); @@ -94,7 +94,7 @@ MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); -module_param(enable_msi, int, 0444); +module_param(enable_msi, bint, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); #ifdef CONFIG_SND_HDA_PATCH_LOADER module_param_array(patch, charp, NULL, 0444); @@ -121,8 +121,8 @@ module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #endif -static bool align_buffer_size = 1; -module_param(align_buffer_size, bool, 0644); +static int align_buffer_size = -1; +module_param(align_buffer_size, bint, 0644); MODULE_PARM_DESC(align_buffer_size, "Force buffer and period sizes to be multiple of 128 bytes."); @@ -148,6 +148,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, PCH}," "{Intel, CPT}," "{Intel, PPT}," + "{Intel, LPT}," "{Intel, PBG}," "{Intel, SCH}," "{ATI, SB450}," @@ -329,6 +330,7 @@ enum { POS_FIX_LPIB, POS_FIX_POSBUF, POS_FIX_VIACOMBO, + POS_FIX_COMBO, }; /* Defines for ATI HD Audio support in SB450 south bridge */ @@ -515,6 +517,7 @@ enum { #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ #define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ +#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -527,7 +530,8 @@ enum { /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI) + (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ + AZX_DCAPS_ALIGN_BUFSIZE) static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", @@ -2347,17 +2351,6 @@ static void azx_power_notify(struct hda_bus *bus) * power management */ -static int snd_hda_codecs_inuse(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - if (snd_hda_codec_needs_resume(codec)) - return 1; - } - return 0; -} - static int azx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); @@ -2404,8 +2397,7 @@ static int azx_resume(struct pci_dev *pci) return -EIO; azx_init_pci(chip); - if (snd_hda_codecs_inuse(chip->bus)) - azx_init_chip(chip, 1); + azx_init_chip(chip, 1); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -2517,6 +2509,7 @@ static int __devinit check_position_fix(struct azx *chip, int fix) case POS_FIX_LPIB: case POS_FIX_POSBUF: case POS_FIX_VIACOMBO: + case POS_FIX_COMBO: return fix; } @@ -2696,6 +2689,12 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->position_fix[0] = chip->position_fix[1] = check_position_fix(chip, position_fix[dev]); + /* combo mode uses LPIB for playback */ + if (chip->position_fix[0] == POS_FIX_COMBO) { + chip->position_fix[0] = POS_FIX_LPIB; + chip->position_fix[1] = POS_FIX_AUTO; + } + check_probe_mask(chip, dev); chip->single_cmd = single_cmd; @@ -2774,9 +2773,16 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } /* disable buffer size rounding to 128-byte multiples if supported */ - chip->align_buffer_size = align_buffer_size; - if (chip->driver_caps & AZX_DCAPS_BUFSIZE) - chip->align_buffer_size = 0; + if (align_buffer_size >= 0) + chip->align_buffer_size = !!align_buffer_size; + else { + if (chip->driver_caps & AZX_DCAPS_BUFSIZE) + chip->align_buffer_size = 0; + else if (chip->driver_caps & AZX_DCAPS_ALIGN_BUFSIZE) + chip->align_buffer_size = 1; + else + chip->align_buffer_size = 1; + } /* allow 64bit DMA address if supported by H/W */ if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) @@ -2992,6 +2998,10 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { { PCI_DEVICE(0x8086, 0x1e20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE}, + /* Lynx Point */ + { PCI_DEVICE(0x8086, 0x8c20), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP | + AZX_DCAPS_BUFSIZE}, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 9d819c4b4923..d68948499fbc 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -19,6 +19,22 @@ #include "hda_local.h" #include "hda_jack.h" +bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) +{ + if (codec->no_jack_detect) + return false; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) + return false; + if (!codec->ignore_misc_bit && + (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE)) + return false; + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) + return false; + return true; +} +EXPORT_SYMBOL_HDA(is_jack_detectable); + /* execute pin sense measurement */ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) { diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index f8f97c71c9c1..c66655cf413a 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -62,18 +62,7 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); -static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) -{ - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) - return false; - if (!codec->ignore_misc_bit && - (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) - return false; - return true; -} +bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, const char *name, int idx); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index aca8d3193b95..0ec9248165bc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -139,10 +139,36 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name); -int snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *slaves); +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char * const *slaves, + const char *suffix, bool init_slave_vol, + struct snd_kcontrol **ctl_ret); +#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ + __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) int snd_hda_codec_reset(struct hda_codec *codec); +enum { + HDA_VMUTE_OFF, + HDA_VMUTE_ON, + HDA_VMUTE_FOLLOW_MASTER, +}; + +struct hda_vmaster_mute_hook { + /* below two fields must be filled by the caller of + * snd_hda_add_vmaster_hook() beforehand + */ + struct snd_kcontrol *sw_kctl; + void (*hook)(void *, int); + /* below are initialized automatically */ + unsigned int mute_mode; /* HDA_VMUTE_XXX */ + struct hda_codec *codec; +}; + +int snd_hda_add_vmaster_hook(struct hda_codec *codec, + struct hda_vmaster_mute_hook *hook, + bool expose_enum_ctl); +void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook); + /* amp value bits */ #define HDA_AMP_MUTE 0x80 #define HDA_AMP_UNMUTE 0x00 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9cb14b42dfff..7143393927da 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -82,6 +82,7 @@ struct ad198x_spec { unsigned int inv_jack_detect: 1;/* inverted jack-detection */ unsigned int inv_eapd: 1; /* inverted EAPD implementation */ unsigned int analog_beep: 1; /* analog beep input present */ + unsigned int avoid_init_slave_vol:1; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; @@ -137,51 +138,17 @@ static int ad198x_init(struct hda_codec *codec) return 0; } -static const char * const ad_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Mono Playback Volume", - "Speaker Playback Volume", - "IEC958 Playback Volume", +static const char * const ad_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Mono", "Speaker", "IEC958", NULL }; -static const char * const ad_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Mono Playback Switch", - "Speaker Playback Switch", - "IEC958 Playback Switch", +static const char * const ad1988_6stack_fp_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", "IEC958", NULL }; -static const char * const ad1988_6stack_fp_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "IEC958 Playback Volume", - NULL -}; - -static const char * const ad1988_6stack_fp_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "IEC958 Playback Switch", - NULL -}; static void ad198x_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -257,10 +224,12 @@ static int ad198x_build_controls(struct hda_codec *codec) unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", + err = __snd_hda_add_vmaster(codec, "Master Playback Volume", vmaster_tlv, (spec->slave_vols ? - spec->slave_vols : ad_slave_vols)); + spec->slave_vols : ad_slave_pfxs), + "Playback Volume", + !spec->avoid_init_slave_vol, NULL); if (err < 0) return err; } @@ -268,7 +237,8 @@ static int ad198x_build_controls(struct hda_codec *codec) err = snd_hda_add_vmaster(codec, "Master Playback Switch", NULL, (spec->slave_sws ? - spec->slave_sws : ad_slave_sws)); + spec->slave_sws : ad_slave_pfxs), + "Playback Switch"); if (err < 0) return err; } @@ -3385,8 +3355,8 @@ static int patch_ad1988(struct hda_codec *codec) if (spec->autocfg.hp_pins[0]) { spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; - spec->slave_vols = ad1988_6stack_fp_slave_vols; - spec->slave_sws = ad1988_6stack_fp_slave_sws; + spec->slave_vols = ad1988_6stack_fp_slave_pfxs; + spec->slave_sws = ad1988_6stack_fp_slave_pfxs; spec->alt_dac_nid = ad1988_alt_dac_nid; spec->stream_analog_alt_playback = &ad198x_pcm_analog_alt_playback; @@ -3594,16 +3564,8 @@ static const struct hda_amp_list ad1884_loopbacks[] = { #endif static const char * const ad1884_slave_vols[] = { - "PCM Playback Volume", - "Mic Playback Volume", - "Mono Playback Volume", - "Front Mic Playback Volume", - "Mic Playback Volume", - "CD Playback Volume", - "Internal Mic Playback Volume", - "Docking Mic Playback Volume", - /* "Beep Playback Volume", */ - "IEC958 Playback Volume", + "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", + "Internal Mic", "Docking Mic", /* "Beep", */ "IEC958", NULL }; @@ -3644,6 +3606,8 @@ static int patch_ad1884(struct hda_codec *codec) spec->vmaster_nid = 0x04; /* we need to cover all playback volumes */ spec->slave_vols = ad1884_slave_vols; + /* slaves may contain input volumes, so we can't raise to 0dB blindly */ + spec->avoid_init_slave_vol = 1; codec->patch_ops = ad198x_patch_ops; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index d29d6d377904..8c6523bbc797 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -70,6 +70,8 @@ struct conexant_spec { const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; + bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -465,21 +467,8 @@ static const struct snd_kcontrol_new cxt_beep_mixer[] = { }; #endif -static const char * const slave_vols[] = { - "Headphone Playback Volume", - "Speaker Playback Volume", - "Front Playback Volume", - "Surround Playback Volume", - "CLFE Playback Volume", - NULL -}; - -static const char * const slave_sws[] = { - "Headphone Playback Switch", - "Speaker Playback Switch", - "Front Playback Switch", - "Surround Playback Switch", - "CLFE Playback Switch", +static const char * const slave_pfxs[] = { + "Headphone", "Speaker", "Front", "Surround", "CLFE", NULL }; @@ -519,14 +508,17 @@ static int conexant_build_controls(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_vols); + vmaster_tlv, slave_pfxs, + "Playback Volume"); if (err < 0) return err; } if (spec->vmaster_nid && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_sws); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); if (err < 0) return err; } @@ -1643,7 +1635,7 @@ static void cxt5051_update_speaker(struct hda_codec *codec) pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); - /* on ideapad there is an aditional speaker (subwoofer) to mute */ + /* on ideapad there is an additional speaker (subwoofer) to mute */ if (spec->ideapad) snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, @@ -3034,7 +3026,6 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO), {} }; @@ -3943,6 +3934,63 @@ static void enable_unsol_pins(struct hda_codec *codec, int num_pins, snd_hda_jack_detect_enable(codec, pins[i], action); } +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + +/* is the given NID found in any of autocfg items? */ +static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + + if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || + found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || + found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || + found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) + return true; + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == nid) + return true; + if (cfg->dig_in_pin == nid) + return true; + return false; +} + +/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + if (!found_in_autocfg(cfg, pin->nid)) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (enabled && spec->pin_eapd_ctrls) { + cx_auto_update_speakers(codec); + return; + } + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); +} + static void cx_auto_init_output(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -3983,6 +4031,7 @@ static void cx_auto_init_output(struct hda_codec *codec) /* turn on all EAPDs if no individual EAPD control is available */ if (!spec->pin_eapd_ctrls) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + clear_unsol_on_unused_pins(codec); } static void cx_auto_init_input(struct hda_codec *codec) @@ -4046,11 +4095,13 @@ static void cx_auto_init_digital(struct hda_codec *codec) static int cx_auto_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); snd_hda_jack_report_sync(codec); + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); return 0; } @@ -4296,6 +4347,13 @@ static int cx_auto_build_controls(struct hda_codec *codec) err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; + if (spec->vmaster_mute.sw_kctl) { + spec->vmaster_mute.hook = cx_auto_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_led); + if (err < 0) + return err; + } return 0; } @@ -4320,7 +4378,6 @@ static int cx_auto_search_adcs(struct hda_codec *codec) return 0; } - static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = conexant_build_pcms, @@ -4368,6 +4425,7 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x17, 0x21a11000 }, /* dock-mic */ { 0x19, 0x2121103f }, /* dock-HP */ + { 0x1c, 0x21440100 }, /* dock SPDIF out */ {} }; @@ -4421,6 +4479,18 @@ static int patch_conexant_auto(struct hda_codec *codec) apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); + /* Show mute-led control only on HP laptops + * This is a sort of white-list: on HP laptops, EAPD corresponds + * only to the mute-LED without actualy amp function. Meanwhile, + * others may use EAPD really as an amp switch, so it might be + * not good to expose it blindly. + */ + switch (codec->subsystem_id >> 16) { + case 0x103c: + spec->vmaster_mute_led = 1; + break; + } + err = cx_auto_search_adcs(codec); if (err < 0) return err; @@ -4434,6 +4504,18 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->patch_ops = cx_auto_patch_ops; if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); + + /* Some laptops with Conexant chips show stalls in S3 resume, + * which falls into the single-cmd mode. + * Better to make reset, then. + */ + if (!codec->bus->sync_write) { + snd_printd("hda_codec: " + "Enable sync_write for stable communication\n"); + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + return 0; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1168ebd3fb5c..540cd13f7f15 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1912,6 +1912,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -1958,6 +1959,7 @@ 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:80862806"); +MODULE_ALIAS("snd-hda-codec-id:80862880"); MODULE_ALIAS("snd-hda-codec-id:808629fb"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 22c73b78ac6f..8ea2fd654327 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -198,8 +198,11 @@ struct alc_spec { /* for virtual master */ hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; #endif /* for PLL fix */ @@ -220,8 +223,6 @@ struct alc_spec { struct snd_array bind_ctls; }; -#define ALC_MODEL_AUTO 0 /* common for all chips */ - static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int bits) { @@ -300,6 +301,9 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, int i, type, num_conns; hda_nid_t nid; + if (!spec->input_mux) + return 0; + mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; if (!imux->num_items && mux_idx > 0) @@ -651,15 +655,51 @@ static void alc_exec_unsol_event(struct hda_codec *codec, int action) snd_hda_jack_report_sync(codec); } +/* update the master volume per volume-knob's unsol event */ +static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value *uctl; + + kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); + if (!kctl) + return; + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return; + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + val &= HDA_AMP_VOLMASK; + uctl->value.integer.value[0] = val; + uctl->value.integer.value[1] = val; + kctl->put(kctl, uctl); + kfree(uctl); +} + /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { + int action; + if (codec->vendor_id == 0x10ec0880) res >>= 28; else res >>= 26; - res = snd_hda_jack_get_action(codec, res); - alc_exec_unsol_event(codec, res); + action = snd_hda_jack_get_action(codec, res); + if (action == ALC_DCVOL_EVENT) { + /* Execute the dc-vol event here as it requires the NID + * but we don't pass NID to alc_exec_unsol_event(). + * Once when we convert all static quirks to the auto-parser, + * this can be integerated into there. + */ + struct hda_jack_tbl *jack; + jack = snd_hda_jack_tbl_get_from_tag(codec, res); + if (jack) + alc_update_knob_master(codec, jack->nid); + return; + } + alc_exec_unsol_event(codec, action); } /* call init functions of standard auto-mute helpers */ @@ -1033,45 +1073,6 @@ static bool alc_check_dyn_adc_switch(struct hda_codec *codec) return true; } -/* rebuild imux for matching with the given auto-mic pins (if not yet) */ -static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux; - static char * const texts[3] = { - "Mic", "Internal Mic", "Dock Mic" - }; - int i; - - if (!spec->auto_mic) - return false; - imux = &spec->private_imux[0]; - if (spec->input_mux == imux) - return true; - spec->imux_pins[0] = spec->ext_mic_pin; - spec->imux_pins[1] = spec->int_mic_pin; - spec->imux_pins[2] = spec->dock_mic_pin; - for (i = 0; i < 3; i++) { - strcpy(imux->items[i].label, texts[i]); - if (spec->imux_pins[i]) { - hda_nid_t pin = spec->imux_pins[i]; - int c; - for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t cap = get_capsrc(spec, c); - int idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - break; - } - } - imux->num_items = i + 1; - } - } - spec->num_mux_defs = 1; - spec->input_mux = imux; - return true; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { @@ -1441,6 +1442,7 @@ enum { ALC_FIXUP_ACT_PRE_PROBE, ALC_FIXUP_ACT_PROBE, ALC_FIXUP_ACT_INIT, + ALC_FIXUP_ACT_BUILD, }; static void alc_apply_fixup(struct hda_codec *codec, int action) @@ -1520,6 +1522,13 @@ static void alc_pick_fixup(struct hda_codec *codec, int id = -1; const char *name = NULL; + /* when model=nofixup is given, don't pick up any fixups */ + if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { + spec->fixup_list = NULL; + spec->fixup_id = -1; + return; + } + if (codec->modelname && models) { while (models->name) { if (!strcmp(codec->modelname, models->name)) { @@ -1847,36 +1856,10 @@ DEFINE_CAPMIX_NOSRC(3); /* * slave controls for virtual master */ -static const char * const alc_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Speaker Playback Volume", - "Mono Playback Volume", - "Line Out Playback Volume", - "CLFE Playback Volume", - "Bass Speaker Playback Volume", - "PCM Playback Volume", - NULL, -}; - -static const char * const alc_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Speaker Playback Switch", - "Mono Playback Switch", - "IEC958 Playback Switch", - "Line Out Playback Switch", - "CLFE Playback Switch", - "Bass Speaker Playback Switch", - "PCM Playback Switch", +static const char * const alc_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Mono", "Line Out", + "CLFE", "Bass Speaker", "PCM", NULL, }; @@ -1967,14 +1950,17 @@ static int __alc_build_controls(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, alc_slave_vols); + vmaster_tlv, alc_slave_pfxs, + "Playback Volume"); if (err < 0) return err; } if (!spec->no_analog && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, alc_slave_sws); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, alc_slave_pfxs, + "Playback Switch", + true, &spec->vmaster_mute.sw_kctl); if (err < 0) return err; } @@ -2059,7 +2045,11 @@ static int alc_build_controls(struct hda_codec *codec) int err = __alc_build_controls(codec); if (err < 0) return err; - return snd_hda_jack_add_kctls(codec, &spec->autocfg); + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + return 0; } @@ -2068,15 +2058,15 @@ static int alc_build_controls(struct hda_codec *codec) */ static void alc_init_special_input_src(struct hda_codec *codec); -static int alc269_fill_coef(struct hda_codec *codec); +static void alc_auto_init_std(struct hda_codec *codec); static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int i; - if (codec->vendor_id == 0x10ec0269) - alc269_fill_coef(codec); + if (spec->init_hook) + spec->init_hook(codec); alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); @@ -2084,9 +2074,7 @@ static int alc_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); alc_init_special_input_src(codec); - - if (spec->init_hook) - spec->init_hook(codec); + alc_auto_init_std(codec); alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); @@ -2675,6 +2663,25 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + /* 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, int ctlidx, @@ -2690,6 +2697,7 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; + add_loopback_list(spec, mix_nid, idx); return 0; } @@ -2954,10 +2962,27 @@ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, return 0; } +static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + int i; + if (found_in_nid_list(nid, spec->multiout.dac_nids, + ARRAY_SIZE(spec->private_dac_nids)) || + found_in_nid_list(nid, spec->multiout.hp_out_nid, + ARRAY_SIZE(spec->multiout.hp_out_nid)) || + found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + return true; + for (i = 0; i < spec->multi_ios; i++) { + if (spec->multi_io[i].dac == nid) + return true; + } + return false; +} + /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { - struct alc_spec *spec = codec->spec; hda_nid_t srcs[5]; int i, num; @@ -2967,16 +2992,8 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); if (!nid) continue; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids))) - continue; - if (found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid))) - continue; - if (found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - continue; - return nid; + if (!alc_is_dac_already_used(codec, nid)) + return nid; } return 0; } @@ -2988,6 +3005,8 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec, hda_nid_t srcs[5]; int i, num; + if (!pin || !dac) + return false; pin = alc_go_down_to_selector(codec, pin); num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); for (i = 0; i < num; i++) { @@ -3000,83 +3019,260 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec, static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { + struct alc_spec *spec = codec->spec; hda_nid_t sel = alc_go_down_to_selector(codec, pin); - if (snd_hda_get_conn_list(codec, sel, NULL) == 1) + hda_nid_t nid, nid_found, srcs[5]; + int i, num = snd_hda_get_connections(codec, sel, srcs, + ARRAY_SIZE(srcs)); + if (num == 1) return alc_auto_look_for_dac(codec, pin); - return 0; + nid_found = 0; + for (i = 0; i < num; i++) { + if (srcs[i] == spec->mixer_nid) + continue; + nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (nid && !alc_is_dac_already_used(codec, nid)) { + if (nid_found) + return 0; + nid_found = nid; + } + } + return nid_found; } -/* return 0 if no possible DAC is found, 1 if one or more found */ -static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs) +/* mark up volume and mute control NIDs: used during badness parsing and + * at creating actual controls + */ +static inline unsigned int get_ctl_pos(unsigned int data) { - int i; + hda_nid_t nid = get_amp_nid_(data); + unsigned int dir; + if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) + return 0; + dir = get_amp_direction_(data); + return (nid << 1) | dir; +} - if (num_outs && !dacs[0]) { - dacs[0] = alc_auto_look_for_dac(codec, pins[0]); - if (!dacs[0]) - return 0; - } +#define is_ctl_used(bits, data) \ + test_bit(get_ctl_pos(data), bits) +#define mark_ctl_usage(bits, data) \ + set_bit(get_ctl_pos(data), bits) - for (i = 1; i < num_outs; i++) - dacs[i] = get_dac_if_single(codec, pins[i]); - for (i = 1; i < num_outs; i++) { +static void clear_vol_marks(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); + memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); +} + +/* badness definition */ +enum { + /* No primary DAC is found for the main output */ + BAD_NO_PRIMARY_DAC = 0x10000, + /* No DAC is found for the extra output */ + BAD_NO_DAC = 0x4000, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x103, + /* No individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x102, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x101, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, +}; + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); +static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); + +static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + unsigned int val; + int badness = 0; + + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (is_ctl_used(spec->vol_ctls, nid)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->vol_ctls, val); + } else + badness += BAD_SHARED_VOL; + nid = alc_look_for_out_mute_nid(codec, pin, dac); + if (nid) { + unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + if (is_ctl_used(spec->sw_ctls, val)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->sw_ctls, val); + } else + badness += BAD_SHARED_VOL; + return badness; +} + +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +}; + +static struct badness_table main_out_badness = { + .no_primary_dac = BAD_NO_PRIMARY_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_PRIMARY_DAC, + .shared_surr = BAD_SHARED_SURROUND, + .shared_clfe = BAD_SHARED_CLFE, + .shared_surr_main = BAD_SHARED_SURROUND, +}; + +static struct badness_table extra_out_badness = { + .no_primary_dac = BAD_NO_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_EXTRA_DAC, + .shared_surr = BAD_SHARED_EXTRA_SURROUND, + .shared_clfe = BAD_SHARED_EXTRA_SURROUND, + .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, +}; + +/* try to assign DACs to pins and return the resultant badness */ +static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, hda_nid_t *dacs, + const struct badness_table *bad) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, j; + int badness = 0; + hda_nid_t dac; + + if (!num_outs) + return 0; + + for (i = 0; i < num_outs; i++) { + hda_nid_t pin = pins[i]; if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pins[i]); + dacs[i] = alc_auto_look_for_dac(codec, pin); + if (!dacs[i] && !i) { + for (j = 1; j < num_outs; j++) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { + dacs[0] = dacs[j]; + dacs[j] = 0; + break; + } + } + } + dac = dacs[i]; + if (!dac) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) + dac = dacs[0]; + else if (cfg->line_outs > i && + alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[i])) + dac = spec->private_dac_nids[i]; + if (dac) { + if (!i) + badness += bad->shared_primary; + else if (i == 1) + badness += bad->shared_surr; + else + badness += bad->shared_clfe; + } else if (alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[0])) { + dac = spec->private_dac_nids[0]; + badness += bad->shared_surr_main; + } else if (!i) + badness += bad->no_primary_dac; + else + badness += bad->no_dac; + } + if (dac) + badness += eval_shared_vol_badness(codec, pin, dac); } - return 1; + + return badness; } static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location, int offset); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); + hda_nid_t reference_pin, + bool hardwired, int offset); + +static bool alc_map_singles(struct hda_codec *codec, int outs, + const hda_nid_t *pins, hda_nid_t *dacs) +{ + int i; + bool found = false; + for (i = 0; i < outs; i++) { + if (dacs[i]) + continue; + dacs[i] = get_dac_if_single(codec, pins[i]); + if (dacs[i]) + found = true; + } + return found; +} /* fill in the dac_nids table from the parsed pin configuration */ -static int alc_auto_fill_dac_nids(struct hda_codec *codec) +static int fill_and_eval_dacs(struct hda_codec *codec, + bool fill_hardwired, + bool fill_mio_first) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int location, defcfg; - int num_pins; - bool redone = false; - int i; + int i, err, badness; - again: /* set num_dacs once to full for alc_auto_look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.hp_out_nid[0] = 0; - spec->multiout.extra_out_nid[0] = 0; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); spec->multiout.dac_nids = spec->private_dac_nids; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); + memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; + clear_vol_marks(codec); + badness = 0; /* fill hard-wired DACs first */ - if (!redone) { - for (i = 0; i < cfg->line_outs; i++) - spec->private_dac_nids[i] = - get_dac_if_single(codec, cfg->line_out_pins[i]); - if (cfg->hp_outs) - spec->multiout.hp_out_nid[0] = - get_dac_if_single(codec, cfg->hp_pins[0]); - if (cfg->speaker_outs) - spec->multiout.extra_out_nid[0] = - get_dac_if_single(codec, cfg->speaker_pins[0]); + if (fill_hardwired) { + bool mapped; + do { + mapped = alc_map_singles(codec, cfg->line_outs, + cfg->line_out_pins, + spec->private_dac_nids); + mapped |= alc_map_singles(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + mapped |= alc_map_singles(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (fill_mio_first && cfg->line_outs == 1 && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + if (!err) + mapped = true; + } + } while (mapped); } - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t pin = cfg->line_out_pins[i]; - if (spec->private_dac_nids[i]) - continue; - spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); - if (!spec->private_dac_nids[i] && !redone) { - /* if we can't find primary DACs, re-probe without - * checking the hard-wired DACs - */ - redone = true; - goto again; - } - } + badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, + spec->private_dac_nids, + &main_out_badness); /* re-count num_dacs and squash invalid entries */ spec->multiout.num_dacs = 0; @@ -3091,30 +3287,144 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) } } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + if (fill_mio_first && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ - defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); - location = get_defcfg_location(defcfg); - - num_pins = alc_auto_fill_multi_ios(codec, location, 0); - if (num_pins > 0) { - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; - } + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + /* we don't count badness at this stage yet */ } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, + spec->multiout.hp_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - /* if no speaker volume is assigned, try again as the primary - * output - */ - if (!err && cfg->speaker_outs > 0 && + err = alc_auto_fill_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + badness += err; + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + /* try multi-ios with HP + inputs */ + int offset = 0; + if (cfg->line_outs >= 3) + offset = 1; + err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, + offset); + if (err < 0) + return err; + badness += err; + } + + if (spec->multi_ios == 2) { + for (i = 0; i < 2; i++) + spec->private_dac_nids[spec->multiout.num_dacs++] = + spec->multi_io[i].dac; + spec->ext_channel_count = 2; + } else if (spec->multi_ios) { + spec->multi_ios = 0; + badness += BAD_MULTI_IO; + } + + return badness; +} + +#define DEBUG_BADNESS + +#ifdef DEBUG_BADNESS +#define debug_badness snd_printdd +#else +#define debug_badness(...) +#endif + +static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) +{ + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[2], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[2], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); +} + +static int alc_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *best_cfg; + int best_badness = INT_MAX; + int badness; + bool fill_hardwired = true, fill_mio_first = true; + bool best_wired = true, best_mio = true; + bool hp_spk_swapped = false; + + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); + if (!best_cfg) + return -ENOMEM; + *best_cfg = *cfg; + + for (;;) { + badness = fill_and_eval_dacs(codec, fill_hardwired, + fill_mio_first); + if (badness < 0) + return badness; + debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, fill_mio_first, + badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + best_mio = fill_mio_first; + } + if (!badness) + break; + fill_mio_first = !fill_mio_first; + if (!fill_mio_first) + continue; + fill_hardwired = !fill_hardwired; + if (!fill_hardwired) + continue; + if (hp_spk_swapped) + break; + hp_spk_swapped = true; + if (cfg->speaker_outs > 0 && cfg->line_out_type == AUTO_PIN_HP_OUT) { cfg->hp_outs = cfg->line_outs; memcpy(cfg->hp_pins, cfg->line_out_pins, @@ -3125,48 +3435,45 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) cfg->speaker_outs = 0; memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - redone = false; - goto again; - } + fill_hardwired = true; + continue; + } + if (cfg->hp_outs > 0 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + fill_hardwired = true; + continue; + } + break; } - if (!spec->multi_ios && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->hp_outs) { - /* try multi-ios with HP + inputs */ - defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]); - location = get_defcfg_location(defcfg); - - num_pins = alc_auto_fill_multi_ios(codec, location, 1); - if (num_pins > 0) { - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; - } + if (badness) { + *cfg = *best_cfg; + fill_and_eval_dacs(codec, best_wired, best_mio); } + debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", + cfg->line_out_type, best_wired, best_mio); + debug_show_configs(spec, cfg); if (cfg->line_out_pins[0]) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], spec->multiout.dac_nids[0]); - return 0; -} -static inline unsigned int get_ctl_pos(unsigned int data) -{ - hda_nid_t nid = get_amp_nid_(data); - unsigned int dir; - if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) - return 0; - dir = get_amp_direction_(data); - return (nid << 1) | dir; + /* clear the bitmap flags for creating controls */ + clear_vol_marks(codec); + kfree(best_cfg); + return 0; } -#define is_ctl_used(bits, data) \ - test_bit(get_ctl_pos(data), bits) -#define mark_ctl_usage(bits, data) \ - set_bit(get_ctl_pos(data), bits) - static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) @@ -3278,14 +3585,17 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, dac = spec->multiout.dac_nids[i]; if (!dac) continue; - if (i >= cfg->line_outs) + if (i >= cfg->line_outs) { pin = spec->multi_io[i - 1].pin; - else + index = 0; + name = channel_name[i]; + } else { pin = cfg->line_out_pins[i]; + name = alc_get_line_out_pfx(spec, i, true, &index); + } sw = alc_look_for_out_mute_nid(codec, pin, dac); vol = alc_look_for_out_vol_nid(codec, pin, dac); - name = alc_get_line_out_pfx(spec, i, true, &index); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); @@ -3382,41 +3692,31 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); } - if (dacs[num_pins - 1]) { - /* OK, we have a multi-output system with individual volumes */ - for (i = 0; i < num_pins; i++) { - if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dacs[i], - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dacs[i], - pfx, i); - } - if (err < 0) - return err; - } - return 0; - } - - /* Let's create a bind-controls */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_sw); - if (!ctl) - return -ENOMEM; - n = 0; for (i = 0; i < num_pins; i++) { - if (get_wcaps(codec, pins[i]) & AC_WCAP_OUT_AMP) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(pins[i], 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_SW, name, 0, (long)ctl); + hda_nid_t dac; + if (dacs[num_pins - 1]) + dac = dacs[i]; /* with individual volumes */ + else + dac = 0; + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { + err = alc_auto_create_extra_out(codec, pins[i], dac, + "Bass Speaker", 0); + } else if (num_pins >= 3) { + snprintf(name, sizeof(name), "%s %s", + pfx, channel_name[i]); + err = alc_auto_create_extra_out(codec, pins[i], dac, + name, 0); + } else { + err = alc_auto_create_extra_out(codec, pins[i], dac, + pfx, i); + } if (err < 0) return err; } + if (dacs[num_pins - 1]) + return 0; + /* Let's create a bind-controls for volumes */ ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); if (!ctl) return -ENOMEM; @@ -3552,58 +3852,111 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) } } +/* check whether the given pin can be a multi-io pin */ +static bool can_be_multiio_pin(struct hda_codec *codec, + unsigned int location, hda_nid_t nid) +{ + unsigned int defcfg, caps; + + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + return false; + if (location && get_defcfg_location(defcfg) != location) + return false; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + return false; + return true; +} + /* * multi-io helper + * + * When hardwired is set, try to fill ony hardwired pins, and returns + * zero if any pins are filled, non-zero if nothing found. + * When hardwired is off, try to fill possible input pins, and returns + * the badness value. */ static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location, - int offset) + hda_nid_t reference_pin, + bool hardwired, int offset) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t prime_dac = spec->private_dac_nids[0]; - int type, i, dacs, num_pins = 0; + int type, i, j, dacs, num_pins, old_pins; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int badness = 0; + + old_pins = spec->multi_ios; + if (old_pins >= 2) + goto end_fill; + + num_pins = 0; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } + } + if (num_pins < 2) + goto end_fill; dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0; - unsigned int defcfg, caps; + if (cfg->inputs[i].type != type) continue; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - continue; - if (location && get_defcfg_location(defcfg) != location) + if (!can_be_multiio_pin(codec, location, nid)) continue; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) + for (j = 0; j < spec->multi_ios; j++) { + if (nid == spec->multi_io[j].pin) + break; + } + if (j < spec->multi_ios) continue; - if (offset && offset + num_pins < dacs) { - dac = spec->private_dac_nids[offset + num_pins]; + + if (offset && offset + spec->multi_ios < dacs) { + dac = spec->private_dac_nids[offset + spec->multi_ios]; if (!alc_auto_is_dac_reachable(codec, nid, dac)) dac = 0; } - if (!dac) + if (hardwired) + dac = get_dac_if_single(codec, nid); + else if (!dac) dac = alc_auto_look_for_dac(codec, nid); - if (!dac) + if (!dac) { + badness++; continue; - spec->multi_io[num_pins].pin = nid; - spec->multi_io[num_pins].dac = dac; - num_pins++; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } + spec->multi_io[spec->multi_ios].pin = nid; + spec->multi_io[spec->multi_ios].dac = dac; + spec->multi_ios++; + if (spec->multi_ios >= 2) + break; } } - spec->multiout.num_dacs = dacs; - if (num_pins < 2) { - /* clear up again */ - memset(spec->private_dac_nids + dacs, 0, - sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs)); - spec->private_dac_nids[0] = prime_dac; - return 0; + end_fill: + if (badness) + badness = BAD_MULTI_IO; + if (old_pins == spec->multi_ios) { + if (hardwired) + return 1; /* nothing found */ + else + return badness; /* no badness if nothing found */ + } + if (!hardwired && spec->multi_ios < 2) { + spec->multi_ios = old_pins; + return badness; } - return num_pins; + + return 0; } static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, @@ -3958,6 +4311,7 @@ static const struct snd_pci_quirk beep_white_list[] = { SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), + SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), {} }; @@ -4057,6 +4411,9 @@ static int alc_parse_auto_config(struct hda_codec *codec, if (spec->kctls.list) add_mixer(spec, spec->kctls.list); + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + return 1; } @@ -4067,26 +4424,47 @@ static int alc880_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc880_loopbacks[] = { - { 0x0b, HDA_INPUT, 0 }, - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 2 }, - { 0x0b, HDA_INPUT, 3 }, - { 0x0b, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - /* * ALC880 fix-ups */ enum { + ALC880_FIXUP_GPIO1, ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, + ALC880_FIXUP_LG, + ALC880_FIXUP_W810, + ALC880_FIXUP_EAPD_COEF, + ALC880_FIXUP_TCL_S700, + ALC880_FIXUP_VOL_KNOB, + ALC880_FIXUP_FUJITSU, + ALC880_FIXUP_F1734, + ALC880_FIXUP_UNIWILL, + ALC880_FIXUP_UNIWILL_DIG, + ALC880_FIXUP_Z71V, + ALC880_FIXUP_3ST_BASE, + ALC880_FIXUP_3ST, + ALC880_FIXUP_3ST_DIG, + ALC880_FIXUP_5ST_BASE, + ALC880_FIXUP_5ST, + ALC880_FIXUP_5ST_DIG, + ALC880_FIXUP_6ST_BASE, + ALC880_FIXUP_6ST, + ALC880_FIXUP_6ST_DIG, }; +/* enable the volume-knob widget support on NID 0x21 */ +static void alc880_fixup_vol_knob(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + if (action == ALC_FIXUP_ACT_PROBE) + snd_hda_jack_detect_enable(codec, 0x21, ALC_DCVOL_EVENT); +} + static const struct alc_fixup alc880_fixups[] = { + [ALC880_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, [ALC880_FIXUP_GPIO2] = { .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, @@ -4101,40 +4479,323 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, + [ALC880_FIXUP_LG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x16, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x1a, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_W810] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_EAPD_COEF] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + {} + }, + }, + [ALC880_FIXUP_TCL_S700] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_VOL_KNOB] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc880_fixup_vol_knob, + }, + [ALC880_FIXUP_FUJITSU] = { + /* override all pins as BIOS on old Amilo is broken */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x01454140 }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_F1734] = { + /* almost compatible with FUJITSU, but no bass and SPDIF */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_UNIWILL] = { + /* need to fix HP and speaker pins to be parsed correctly */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { } + }, + }, + [ALC880_FIXUP_UNIWILL_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1f, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_Z71V] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x99030120 }, /* speaker */ + { 0x15, 0x0121411f }, /* HP */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19950 }, /* mic-in */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + } + }, + [ALC880_FIXUP_3ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* line-out */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_3ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_3ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_5ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01016412 }, /* surr */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_5ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_5ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_6ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x01016412 }, /* surr */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01012414 }, /* side */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x02a19c40 }, /* front-mic */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x0121411f }, /* HP */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_6ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, }; static const struct snd_pci_quirk alc880_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), + + /* Below is the copied entries from alc880_quirks.c. + * It's not quite sure whether BIOS sets the correct pin-config table + * on these machines, thus they are kept to be compatible with + * the old static quirks. Once when it's confirmed to work without + * these overrides, it'd be better to remove. + */ + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), + /* default Intel */ + SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), {} }; +static const struct alc_model_fixup alc880_fixup_models[] = { + {.id = ALC880_FIXUP_3ST, .name = "3stack"}, + {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, + {.id = ALC880_FIXUP_5ST, .name = "5stack"}, + {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, + {.id = ALC880_FIXUP_6ST, .name = "6stack"}, + {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, + {} +}; -/* - * board setups - */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#define alc_board_config \ - snd_hda_check_board_config -#define alc_board_codec_sid_config \ - snd_hda_check_board_codec_sid_config -#include "alc_quirks.c" -#else -#define alc_board_config(codec, nums, models, tbl) -1 -#define alc_board_codec_sid_config(codec, nums, models, tbl) -1 -#define setup_preset(codec, x) /* NOP */ -#endif /* * OK, here we have finally the patch for ALC880 */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc880_quirks.c" -#endif - static int patch_alc880(struct hda_codec *codec) { struct alc_spec *spec; - int board_config; int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -4146,47 +4807,14 @@ static int patch_alc880(struct hda_codec *codec) spec->mixer_nid = 0x0b; spec->need_dac_fix = 1; - board_config = alc_board_config(codec, ALC880_MODEL_LAST, - alc880_models, alc880_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc880_fixup_tbl, alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS - else if (!err) { - printk(KERN_INFO - "hda_codec: Cannot set up configuration " - "from BIOS. Using 3-stack mode...\n"); - board_config = ALC880_3ST; - } -#endif - } - - if (board_config != ALC_MODEL_AUTO) { - spec->vmaster_nid = 0x0c; - setup_preset(codec, &alc880_presets[board_config]); - } - - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } + alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + alc880_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) + goto error; if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); @@ -4195,17 +4823,9 @@ static int patch_alc880(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc880_loopbacks; -#endif + + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -4225,49 +4845,115 @@ static int alc260_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc260_loopbacks[] = { - { 0x07, HDA_INPUT, 0 }, - { 0x07, HDA_INPUT, 1 }, - { 0x07, HDA_INPUT, 2 }, - { 0x07, HDA_INPUT, 3 }, - { 0x07, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - /* * Pin config fixes */ enum { - PINFIX_HP_DC5750, + ALC260_FIXUP_HP_DC5750, + ALC260_FIXUP_HP_PIN_0F, + ALC260_FIXUP_COEF, + ALC260_FIXUP_GPIO1, + ALC260_FIXUP_GPIO1_TOGGLE, + ALC260_FIXUP_REPLACER, + ALC260_FIXUP_HP_B1900, }; +static void alc260_gpio1_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->hp_jack_present); +} + +static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == ALC_FIXUP_ACT_PROBE) { + /* although the machine has only one output pin, we need to + * toggle GPIO1 according to the jack state + */ + spec->automute_hook = alc260_gpio1_automute; + spec->detect_hp = 1; + spec->automute_speaker = 1; + spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); + spec->unsol_event = alc_sku_unsol_event; + add_verb(codec->spec, alc_gpio1_init_verbs); + } +} + static const struct alc_fixup alc260_fixups[] = { - [PINFIX_HP_DC5750] = { + [ALC260_FIXUP_HP_DC5750] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, + [ALC260_FIXUP_HP_PIN_0F] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x0f, 0x01214000 }, /* HP */ + { } + } + }, + [ALC260_FIXUP_COEF] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, + [ALC260_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, + [ALC260_FIXUP_GPIO1_TOGGLE] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, + [ALC260_FIXUP_REPLACER] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, + }, + [ALC260_FIXUP_HP_B1900] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_COEF, + } }; static const struct snd_pci_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750), + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {} }; /* */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc260_quirks.c" -#endif - static int patch_alc260(struct hda_codec *codec) { struct alc_spec *spec; - int err, board_config; + int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -4277,47 +4963,13 @@ static int patch_alc260(struct hda_codec *codec) spec->mixer_nid = 0x07; - board_config = alc_board_config(codec, ALC260_MODEL_LAST, - alc260_models, alc260_cfg_tbl); - if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS - else if (!err) { - printk(KERN_INFO - "hda_codec: Cannot set up configuration " - "from BIOS. Using base mode...\n"); - board_config = ALC260_BASIC; - } -#endif - } - - if (board_config != ALC_MODEL_AUTO) { - setup_preset(codec, &alc260_presets[board_config]); - spec->vmaster_nid = 0x08; - } - - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } + alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); + /* automatic parse from the BIOS config */ + err = alc260_parse_auto_config(codec); + if (err < 0) + goto error; if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); @@ -4326,18 +4978,10 @@ static int patch_alc260(struct hda_codec *codec) set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc260_loopbacks; -#endif + + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -4358,9 +5002,6 @@ static int patch_alc260(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc882_loopbacks alc880_loopbacks -#endif /* * Pin config fixes @@ -4377,6 +5018,8 @@ enum { ALC882_FIXUP_EAPD, ALC883_FIXUP_EAPD, ALC883_FIXUP_ACER_EAPD, + ALC882_FIXUP_GPIO1, + ALC882_FIXUP_GPIO2, ALC882_FIXUP_GPIO3, ALC889_FIXUP_COEF, ALC882_FIXUP_ASUS_W2JC, @@ -4385,6 +5028,8 @@ enum { ALC882_FIXUP_ASPIRE_8930G_VERBS, ALC885_FIXUP_MACPRO_GPIO, ALC889_FIXUP_DAC_ROUTE, + ALC889_FIXUP_MBP_VREF, + ALC889_FIXUP_IMAC91_VREF, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -4463,6 +5108,51 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, } } +/* Set VREF on HP pin */ +static void alc889_fixup_mbp_vref(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t nids[2] = { 0x14, 0x15 }; + int i; + + if (action != ALC_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); + if (get_defcfg_device(val) != AC_JACK_HP_OUT) + continue; + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val |= AC_PINCTL_VREF_80; + snd_hda_codec_write(codec, nids[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + spec->keep_vref_in_automute = 1; + break; + } +} + +/* Set VREF on speaker pins on imac91 */ +static void alc889_fixup_imac91_vref(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t nids[2] = { 0x18, 0x1a }; + int i; + + if (action != ALC_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val; + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val |= AC_PINCTL_VREF_50; + snd_hda_codec_write(codec, nids[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + } + spec->keep_vref_in_automute = 1; +} + static const struct alc_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = ALC_FIXUP_PINS, @@ -4548,6 +5238,14 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [ALC882_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, + [ALC882_FIXUP_GPIO2] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio2_init_verbs, + }, [ALC882_FIXUP_GPIO3] = { .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio3_init_verbs, @@ -4621,6 +5319,18 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_dac_route, }, + [ALC889_FIXUP_MBP_VREF] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_IMAC91_VREF] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc889_fixup_imac91_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -4654,11 +5364,26 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), /* All Apple entries are in codec SSIDs */ + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO), SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF), SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), @@ -4684,14 +5409,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc882_quirks.c" -#endif - static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; - int err, board_config; + int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -4715,45 +5436,15 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error; - board_config = alc_board_config(codec, ALC882_MODEL_LAST, - alc882_models, NULL); - if (board_config < 0) - board_config = alc_board_codec_sid_config(codec, - ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl); - - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } + alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); - if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - } - - if (board_config != ALC_MODEL_AUTO) { - setup_preset(codec, &alc882_presets[board_config]); - spec->vmaster_nid = 0x0c; - } - - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); + /* automatic parse from the BIOS config */ + err = alc882_parse_auto_config(codec); + if (err < 0) + goto error; if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); @@ -4762,18 +5453,9 @@ static int patch_alc882(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc882_loopbacks; -#endif + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -4869,10 +5551,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc262_loopbacks alc880_loopbacks -#endif - /* */ static int patch_alc262(struct hda_codec *codec) @@ -4912,15 +5590,6 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -4928,16 +5597,10 @@ static int patch_alc262(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc262_loopbacks; -#endif + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -5031,17 +5694,7 @@ static int patch_alc268(struct hda_codec *codec) (0 << AC_AMPCAP_MUTE_SHIFT)); } - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; return 0; @@ -5054,10 +5707,6 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc269_loopbacks alc880_loopbacks -#endif - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -5079,35 +5728,6 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { /* NID is set in alc_build_pcms */ }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int alc269_mic2_for_mute_led(struct hda_codec *codec) -{ - switch (codec->subsystem_id) { - case 0x103c1586: - return 1; - } - return 0; -} - -static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) -{ - /* update mute-LED according to the speaker mute state */ - if (nid == 0x01 || nid == 0x14) { - int pinval; - if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE) - pinval = 0x24; - else - pinval = 0x20; - /* mic2 vref pin is used for mute LED control */ - snd_hda_codec_update_cache(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinval); - } - return alc_check_power_status(codec, nid); -} -#endif /* CONFIG_SND_HDA_POWER_SAVE */ - /* different alc269-variants */ enum { ALC269_TYPE_ALC269VA, @@ -5258,6 +5878,31 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, spec->automute_hook = alc269_quanta_automute; } +/* update mute-LED according to the speaker mute state via mic2 VREF pin */ +static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + unsigned int pinval = enabled ? 0x20 : 0x24; + snd_hda_codec_update_cache(codec, 0x19, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinval); +} + +static void alc269_fixup_mic2_mute(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + switch (action) { + case ALC_FIXUP_ACT_BUILD: + spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + /* fallthru */ + case ALC_FIXUP_ACT_INIT: + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + break; + } +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -5275,6 +5920,7 @@ enum { ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, + ALC269_FIXUP_MIC2_MUTE_LED, }; static const struct alc_fixup alc269_fixups[] = { @@ -5395,9 +6041,14 @@ static const struct alc_fixup alc269_fixups[] = { { } }, }, + [ALC269_FIXUP_MIC2_MUTE_LED] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_mic2_mute, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), @@ -5420,7 +6071,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), -#if 1 +#if 0 /* Below is a quirk table taken from the old code. * Basically the device should work as is without the fixup table. * If BIOS doesn't give a proper info, enable the corresponding @@ -5478,13 +6129,13 @@ static const struct alc_model_fixup alc269_fixup_models[] = { }; -static int alc269_fill_coef(struct hda_codec *codec) +static void alc269_fill_coef(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int val; if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return 0; + return; if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { alc_write_coef_idx(codec, 0xf, 0x960b); @@ -5520,8 +6171,6 @@ static int alc269_fill_coef(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x4); /* HP */ alc_write_coef_idx(codec, 0x4, val | (1<<11)); - - return 0; } /* @@ -5565,6 +6214,7 @@ static int patch_alc269(struct hda_codec *codec) } if (err < 0) goto error; + spec->init_hook = alc269_fill_coef; alc269_fill_coef(codec); } @@ -5577,15 +6227,6 @@ static int patch_alc269(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -5593,21 +6234,13 @@ static int patch_alc269(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; #ifdef CONFIG_PM codec->patch_ops.resume = alc269_resume; #endif - spec->init_hook = alc_auto_init_std; spec->shutup = alc269_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc269_loopbacks; - if (alc269_mic2_for_mute_led(codec)) - codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; -#endif + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -5627,21 +6260,12 @@ static int alc861_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc861_loopbacks[] = { - { 0x15, HDA_INPUT, 0 }, - { 0x15, HDA_INPUT, 1 }, - { 0x15, HDA_INPUT, 2 }, - { 0x15, HDA_INPUT, 3 }, - { } /* end */ -}; -#endif - - /* Pin config fixes */ enum { - PINFIX_FSC_AMILO_PI1505, - PINFIX_ASUS_A6RP, + ALC861_FIXUP_FSC_AMILO_PI1505, + ALC861_FIXUP_AMP_VREF_0F, + ALC861_FIXUP_NO_JACK_DETECT, + ALC861_FIXUP_ASUS_A6RP, }; /* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ @@ -5663,8 +6287,16 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, spec->keep_vref_in_automute = 1; } +/* suppress the jack-detection */ +static void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + if (action == ALC_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; +} + static const struct alc_fixup alc861_fixups[] = { - [PINFIX_FSC_AMILO_PI1505] = { + [ALC861_FIXUP_FSC_AMILO_PI1505] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { { 0x0b, 0x0221101f }, /* HP */ @@ -5672,17 +6304,29 @@ static const struct alc_fixup alc861_fixups[] = { { } } }, - [PINFIX_ASUS_A6RP] = { + [ALC861_FIXUP_AMP_VREF_0F] = { .type = ALC_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, + [ALC861_FIXUP_NO_JACK_DETECT] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC861_FIXUP_ASUS_A6RP] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + .chained = true, + .chain_id = ALC861_FIXUP_NO_JACK_DETECT, + } }; static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), {} }; @@ -5709,15 +6353,6 @@ static int patch_alc861(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) @@ -5725,16 +6360,13 @@ static int patch_alc861(struct hda_codec *codec) set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->power_hook = alc_power_eapd; - if (!spec->loopback.amplist) - spec->loopback.amplist = alc861_loopbacks; #endif + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0; error: @@ -5749,10 +6381,6 @@ static int patch_alc861(struct hda_codec *codec) * * In addition, an independent DAC */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc861vd_loopbacks alc880_loopbacks -#endif - static int alc861vd_parse_auto_config(struct hda_codec *codec) { static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; @@ -5833,15 +6461,6 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); } - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) @@ -5849,16 +6468,11 @@ static int patch_alc861vd(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc861vd_loopbacks; -#endif + + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -5878,9 +6492,6 @@ static int patch_alc861vd(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc662_loopbacks alc880_loopbacks -#endif /* * BIOS auto configuration @@ -5930,6 +6541,7 @@ enum { ALC662_FIXUP_ASUS_MODE6, ALC662_FIXUP_ASUS_MODE7, ALC662_FIXUP_ASUS_MODE8, + ALC662_FIXUP_NO_JACK_DETECT, }; static const struct alc_fixup alc662_fixups[] = { @@ -6075,6 +6687,10 @@ static const struct alc_fixup alc662_fixups[] = { .chained = true, .chain_id = ALC662_FIXUP_SKU_IGNORE }, + [ALC662_FIXUP_NO_JACK_DETECT] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6083,6 +6699,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), + SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), @@ -6204,15 +6821,6 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -6232,16 +6840,10 @@ static int patch_alc662(struct hda_codec *codec) } } - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc662_loopbacks; -#endif + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -6281,11 +6883,7 @@ static int patch_alc680(struct hda_codec *codec) return err; } - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9dbb5735d778..33a9946b492c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -99,6 +99,7 @@ enum { STAC_DELL_VOSTRO_3500, STAC_92HD83XXX_HP_cNB11_INTQUAD, STAC_HP_DV7_4000, + STAC_HP_ZEPHYR, STAC_92HD83XXX_MODELS }; @@ -309,6 +310,8 @@ struct sigmatel_spec { unsigned long auto_capvols[MAX_ADCS_NUM]; unsigned auto_dmic_cnt; hda_nid_t auto_dmic_nids[MAX_DMICS_NUM]; + + struct hda_vmaster_mute_hook vmaster_mute; }; static const hda_nid_t stac9200_adc_nids[1] = { @@ -662,7 +665,6 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE static int stac_vrefout_set(struct hda_codec *codec, hda_nid_t nid, unsigned int new_vref) { @@ -686,7 +688,6 @@ static int stac_vrefout_set(struct hda_codec *codec, return 1; } -#endif static unsigned int stac92xx_vref_set(struct hda_codec *codec, hda_nid_t nid, unsigned int new_vref) @@ -894,6 +895,13 @@ static const struct hda_verb stac92hd83xxx_core_init[] = { {} }; +static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { + { 0x22, 0x785, 0x43 }, + { 0x22, 0x782, 0xe0 }, + { 0x22, 0x795, 0x00 }, + {} +}; + static const struct hda_verb stac92hd71bxx_core_init[] = { /* set master volume and direct control */ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -999,8 +1007,8 @@ static const struct hda_verb stac9205_core_init[] = { } static const struct snd_kcontrol_new stac9200_mixer[] = { - 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_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), { } /* end */ @@ -1027,8 +1035,8 @@ static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = { }; static const struct snd_kcontrol_new stac925x_mixer[] = { - HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT), { } /* end */ }; @@ -1060,34 +1068,25 @@ static struct snd_kcontrol_new stac_smux_mixer = { .put = stac92xx_smux_enum_put, }; -static const char * const slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Speaker Playback Volume", +static const char * const slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "IEC958", NULL }; -static const char * const slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Speaker Playback Switch", - "IEC958 Playback Switch", - NULL -}; +static void stac92xx_update_led_status(struct hda_codec *codec, int enabled); + +static void stac92xx_vmaster_hook(void *private_data, int val) +{ + stac92xx_update_led_status(private_data, val); +} static void stac92xx_free_kctls(struct hda_codec *codec); static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; + unsigned int vmaster_tlv[4]; int err; int i; @@ -1144,22 +1143,28 @@ static int stac92xx_build_controls(struct hda_codec *codec) } /* if we have no master control, let's create it */ - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - 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) - return err; - } - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_sws); + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + 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_pfxs, + "Playback Volume"); + if (err < 0) + return err; + + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); + if (err < 0) + return err; + + if (spec->gpio_led) { + spec->vmaster_mute.hook = stac92xx_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); if (err < 0) return err; } @@ -1636,6 +1641,12 @@ static const unsigned int hp_dv7_4000_pin_configs[10] = { 0x40f000f0, 0x40f000f0, }; +static const unsigned int hp_zephyr_pin_configs[10] = { + 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, + 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, + 0, 0, +}; + static const unsigned int hp_cNB11_intquad_pin_configs[10] = { 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, @@ -1649,6 +1660,7 @@ static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs, [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, + [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, }; static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { @@ -1659,6 +1671,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", [STAC_HP_DV7_4000] = "hp-dv7-4000", + [STAC_HP_ZEPHYR] = "hp-zephyr", }; static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1711,6 +1724,14 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, + "HP", STAC_HP_ZEPHYR), + {} /* terminator */ +}; + +static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, + "HP", STAC_HP_ZEPHYR), {} /* terminator */ }; @@ -4410,8 +4431,7 @@ static int stac92xx_init(struct hda_codec *codec) snd_hda_jack_report_sync(codec); /* sync mute LED */ - if (spec->gpio_led) - hda_call_check_power_status(codec, 0x01); + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); if (spec->dac_list) stac92xx_power_down(codec); return 0; @@ -4989,7 +5009,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE static int stac92xx_pre_resume(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -5024,82 +5043,40 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, afg_power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state, true); } +#else +#define stac92xx_suspend NULL +#define stac92xx_resume NULL +#define stac92xx_pre_resume NULL +#define stac92xx_set_power_state NULL +#endif /* CONFIG_PM */ -/* - * For this feature CONFIG_SND_HDA_POWER_SAVE is needed - * as mute LED state is updated in check_power_status hook - */ -static int stac92xx_update_led_status(struct hda_codec *codec) +/* update mute-LED accoring to the master switch */ +static void stac92xx_update_led_status(struct hda_codec *codec, int enabled) { struct sigmatel_spec *spec = codec->spec; - int i, num_ext_dacs, muted = 1; - unsigned int muted_lvl, notmtd_lvl; - hda_nid_t nid; + int muted = !enabled; if (!spec->gpio_led) - return 0; + return; + + /* LED state is inverted on these systems */ + if (spec->gpio_led_polarity) + muted = !muted; - for (i = 0; i < spec->multiout.num_dacs; i++) { - nid = spec->multiout.dac_nids[i]; - if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE)) { - muted = 0; /* something heard */ - break; - } - } - if (muted && spec->multiout.hp_nid) - if (!(snd_hda_codec_amp_read(codec, - spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE)) { - muted = 0; /* HP is not muted */ - } - num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid); - for (i = 0; muted && i < num_ext_dacs; i++) { - nid = spec->multiout.extra_out_nid[i]; - if (nid == 0) - break; - if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE)) { - muted = 0; /* extra output is not muted */ - } - } /*polarity defines *not* muted state level*/ if (!spec->vref_mute_led_nid) { if (muted) spec->gpio_data &= ~spec->gpio_led; /* orange */ else spec->gpio_data |= spec->gpio_led; /* white */ - - if (!spec->gpio_led_polarity) { - /* LED state is inverted on these systems */ - spec->gpio_data ^= spec->gpio_led; - } stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); } else { - notmtd_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; - muted_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_50; - spec->vref_led = muted ? muted_lvl : notmtd_lvl; + spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; stac_vrefout_set(codec, spec->vref_mute_led_nid, spec->vref_led); } - return 0; -} - -/* - * use power check for controlling mute led of HP notebooks - */ -static int stac92xx_check_power_status(struct hda_codec *codec, - hda_nid_t nid) -{ - stac92xx_update_led_status(codec); - - return 0; } -#endif /* CONFIG_SND_HDA_POWER_SAVE */ -#endif /* CONFIG_PM */ static const struct hda_codec_ops stac92xx_patch_ops = { .build_controls = stac92xx_build_controls, @@ -5580,6 +5557,12 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) STAC_92HD83XXX_MODELS, stac92hd83xxx_models, stac92hd83xxx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD83XXX_MODELS, stac92hd83xxx_models, + stac92hd83xxx_codec_id_cfg_tbl); again: if (spec->board_config < 0) snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", @@ -5590,12 +5573,17 @@ again: codec->patch_ops = stac92xx_patch_ops; + switch (spec->board_config) { + case STAC_HP_ZEPHYR: + spec->init = stac92hd83xxx_hp_zephyr_init; + break; + } + if (find_mute_led_cfg(codec, -1/*no default cfg*/)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, spec->gpio_led_polarity); -#ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { if (!spec->vref_mute_led_nid) { spec->gpio_mask |= spec->gpio_led; @@ -5605,11 +5593,10 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } +#ifdef CONFIG_PM codec->patch_ops.pre_resume = stac92xx_pre_resume; - codec->patch_ops.check_power_status = - stac92xx_check_power_status; +#endif } -#endif err = stac92xx_parse_auto_config(codec); if (!err) { @@ -5906,7 +5893,6 @@ again: spec->gpio_led, spec->gpio_led_polarity); -#ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { if (!spec->vref_mute_led_nid) { spec->gpio_mask |= spec->gpio_led; @@ -5916,11 +5902,10 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } +#ifdef CONFIG_PM codec->patch_ops.pre_resume = stac92xx_pre_resume; - codec->patch_ops.check_power_status = - stac92xx_check_power_status; +#endif } -#endif spec->multiout.dac_nids = spec->dac_nids; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index dff9a00ee8fb..06214fdc9486 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -550,7 +550,10 @@ static void via_auto_init_output(struct hda_codec *codec, pin = path->path[path->depth - 1]; init_output_pin(codec, pin, pin_type); - caps = query_amp_caps(codec, pin, HDA_OUTPUT); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + else + caps = 0; if (caps & AC_AMPCAP_MUTE) { unsigned int val; val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; @@ -645,6 +648,10 @@ static void via_auto_init_analog_input(struct hda_codec *codec) /* init ADCs */ for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t nid = spec->adc_nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || + !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) + continue; snd_hda_codec_write(codec, spec->adc_nids[i], 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)); @@ -1445,25 +1452,9 @@ static const struct hda_pcm_stream via_pcm_digital_capture = { /* * slave controls for virtual master */ -static const char * const via_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Speaker Playback Volume", - NULL, -}; - -static const char * const via_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Speaker Playback Switch", +static const char * const via_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", NULL, }; @@ -1508,13 +1499,15 @@ static int via_build_controls(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, via_slave_vols); + vmaster_tlv, via_slave_pfxs, + "Playback Volume"); if (err < 0) return err; } if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, via_slave_sws); + NULL, via_slave_pfxs, + "Playback Switch"); if (err < 0) return err; } @@ -1522,6 +1515,8 @@ static int via_build_controls(struct hda_codec *codec) /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { + if (!spec->mux_nids[i]) + continue; err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); if (err < 0) return err; @@ -2488,6 +2483,8 @@ static int create_mic_boost_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; int i, err; for (i = 0; i < cfg->num_inputs; i++) { @@ -2502,8 +2499,13 @@ static int create_mic_boost_ctls(struct hda_codec *codec) if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) continue; label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; snprintf(name, sizeof(name), "%s Boost Volume", label); - 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(pin, 3, 0, HDA_INPUT)); if (err < 0) return err; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 92362973764d..812d10e43ae0 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1013,6 +1013,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice, ice->hw_rates); } +/* if the card has the internal rate locked (is_pro_locked), limit runtime + hw rates to the current internal rate only. +*/ +static void constrain_rate_if_locked(struct snd_pcm_substream *substream) +{ + struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int rate; + if (is_pro_rate_locked(ice)) { + rate = ice->get_rate(ice); + if (rate >= runtime->hw.rate_min + && rate <= runtime->hw.rate_max) { + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } + } +} + + /* multi-channel playback needs alignment 8x32bit regardless of the channels * actually used */ @@ -1046,6 +1065,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + constrain_rate_if_locked(substream); if (ice->pro_open) ice->pro_open(ice, substream); return 0; @@ -1066,6 +1086,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + constrain_rate_if_locked(substream); if (ice->pro_open) ice->pro_open(ice, substream); return 0; @@ -1215,6 +1236,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + constrain_rate_if_locked(substream); if (ice->spdif.ops.open) ice->spdif.ops.open(ice, substream); return 0; @@ -1251,6 +1273,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + constrain_rate_if_locked(substream); if (ice->spdif.ops.open) ice->spdif.ops.open(ice, substream); return 0; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 12a9a2b03387..a8159b81e9c4 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2317,6 +2317,10 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, + &chip->saved_dsxg_legacy); + pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY, + &chip->saved_dsxg_elegacy); snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); snd_ymfpci_disable_dsp(chip); @@ -2351,6 +2355,11 @@ int snd_ymfpci_resume(struct pci_dev *pci) snd_ac97_resume(chip->ac97); + pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, + chip->saved_dsxg_legacy); + pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, + chip->saved_dsxg_elegacy); + /* start hw again */ if (chip->start_count > 0) { spin_lock_irq(&chip->reg_lock); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 9685dff44dd8..fe7fbaeb7146 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2181,26 +2181,9 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { switch (control->type) { - case WM8994: - if (wm8994->revision < 4) { - /* Tweak DC servo and DSP - * configuration for improved - * performance. */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x56, 0x3); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); - } - break; - case WM8958: if (wm8994->revision == 0) { /* Optimise performance for rev A */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0xcb, 0x81); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); - snd_soc_update_bits(codec, WM8958_CHARGE_PUMP_2, WM8958_CP_DISCH, @@ -2208,13 +2191,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, } break; - case WM1811: - if (wm8994->revision < 2) { - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x5d, 0x7e); - snd_soc_write(codec, 0x5e, 0x0); - snd_soc_write(codec, 0x102, 0x0); - } + default: break; } diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 47b23fea20c2..e00dd0b1139c 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -7,7 +7,6 @@ config SND_OMAP_SOC_DMIC config SND_OMAP_SOC_MCBSP tristate - select OMAP_MCBSP config SND_OMAP_SOC_MCPDM tristate @@ -27,7 +26,6 @@ config SND_OMAP_SOC_N810 config SND_OMAP_SOC_RX51 tristate "SoC Audio support for Nokia RX-51" depends on SND_OMAP_SOC && MACH_NOKIA_RX51 - select OMAP_MCBSP select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 123ac18303e5..1d656bce01d4 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -1,7 +1,7 @@ # OMAP Platform Support snd-soc-omap-objs := omap-pcm.o snd-soc-omap-dmic-objs := omap-dmic.o -snd-soc-omap-mcbsp-objs := omap-mcbsp.o +snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o snd-soc-omap-hdmi-objs := omap-hdmi.o diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c index add4866d7e67..009533ab8d18 100644 --- a/sound/soc/omap/am3517evm.c +++ b/sound/soc/omap/am3517evm.c @@ -95,7 +95,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static struct snd_soc_dai_link am3517evm_dai = { .name = "TLV320AIC23", .stream_name = "AIC23", - .cpu_dai_name ="omap-mcbsp-dai.0", + .cpu_dai_name = "omap-mcbsp.1", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "omap-pcm-audio", .codec_name = "tlv320aic23-codec.2-001a", diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 41586b26ce90..7d4fa8ed6699 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -558,7 +558,7 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link ams_delta_dai_link = { .name = "CX20442", .stream_name = "CX20442", - .cpu_dai_name ="omap-mcbsp-dai.0", + .cpu_dai_name = "omap-mcbsp.1", .codec_dai_name = "cx20442-voice", .init = ams_delta_cx20442_init, .platform_name = "omap-pcm-audio", diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c index ccae58a1339c..e8357819175b 100644 --- a/sound/soc/omap/igep0020.c +++ b/sound/soc/omap/igep0020.c @@ -60,7 +60,7 @@ static struct snd_soc_ops igep2_ops = { static struct snd_soc_dai_link igep2_dai = { .name = "TWL4030", .stream_name = "TWL4030", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c new file mode 100644 index 000000000000..e5f44440d1b9 --- /dev/null +++ b/sound/soc/omap/mcbsp.c @@ -0,0 +1,1040 @@ +/* + * sound/soc/omap/mcbsp.c + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz <samuel.ortiz@nokia.com> + * + * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> + * Peter Ujfalusi <peter.ujfalusi@ti.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. + * + * Multichannel mode not supported. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include <plat/mcbsp.h> + +#include "mcbsp.h" + +static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; + __raw_writew((u16)val, addr); + } else { + ((u32 *)mcbsp->reg_cache)[reg] = val; + __raw_writel(val, addr); + } +} + +static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + return !from_cache ? __raw_readw(addr) : + ((u16 *)mcbsp->reg_cache)[reg]; + } else { + return !from_cache ? __raw_readl(addr) : + ((u32 *)mcbsp->reg_cache)[reg]; + } +} + +static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + __raw_writel(val, mcbsp->st_data->io_base_st + reg); +} + +static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) +{ + return __raw_readl(mcbsp->st_data->io_base_st + reg); +} + +#define MCBSP_READ(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) +#define MCBSP_WRITE(mcbsp, reg, val) \ + omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) +#define MCBSP_READ_CACHE(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) + +#define MCBSP_ST_READ(mcbsp, reg) \ + omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) +#define MCBSP_ST_WRITE(mcbsp, reg, val) \ + omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) + +static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) +{ + dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); + dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", + MCBSP_READ(mcbsp, DRR2)); + dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", + MCBSP_READ(mcbsp, DRR1)); + dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", + MCBSP_READ(mcbsp, DXR2)); + dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", + MCBSP_READ(mcbsp, DXR1)); + dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", + MCBSP_READ(mcbsp, SPCR2)); + dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", + MCBSP_READ(mcbsp, SPCR1)); + dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", + MCBSP_READ(mcbsp, RCR2)); + dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", + MCBSP_READ(mcbsp, RCR1)); + dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", + MCBSP_READ(mcbsp, XCR2)); + dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", + MCBSP_READ(mcbsp, XCR1)); + dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", + MCBSP_READ(mcbsp, SRGR2)); + dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", + MCBSP_READ(mcbsp, SRGR1)); + dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", + MCBSP_READ(mcbsp, PCR0)); + dev_dbg(mcbsp->dev, "***********************\n"); +} + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) +{ + struct omap_mcbsp *mcbsp_tx = dev_id; + u16 irqst_spcr2; + + irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2); + dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + + if (irqst_spcr2 & XSYNC_ERR) { + dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n", + irqst_spcr2); + /* Writing zero to XSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id) +{ + struct omap_mcbsp *mcbsp_rx = dev_id; + u16 irqst_spcr1; + + irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1); + dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + + if (irqst_spcr1 & RSYNC_ERR) { + dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n", + irqst_spcr1); + /* Writing zero to RSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1)); + } + + return IRQ_HANDLED; +} + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ +void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config) +{ + dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", + mcbsp->id, mcbsp->phys_base); + + /* We write the given config */ + MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); + MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); + MCBSP_WRITE(mcbsp, RCR2, config->rcr2); + MCBSP_WRITE(mcbsp, RCR1, config->rcr1); + MCBSP_WRITE(mcbsp, XCR2, config->xcr2); + MCBSP_WRITE(mcbsp, XCR1, config->xcr1); + MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); + MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); + MCBSP_WRITE(mcbsp, MCR2, config->mcr2); + MCBSP_WRITE(mcbsp, MCR1, config->mcr1); + MCBSP_WRITE(mcbsp, PCR0, config->pcr0); + if (mcbsp->pdata->has_ccr) { + MCBSP_WRITE(mcbsp, XCCR, config->xccr); + MCBSP_WRITE(mcbsp, RCCR, config->rccr); + } + /* Enable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); +} + +/** + * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register + * @id - mcbsp id + * @stream - indicates the direction of data flow (rx or tx) + * + * Returns the address of mcbsp data transmit register or data receive register + * to be used by DMA for transferring/receiving data based on the value of + * @stream for the requested mcbsp given by @id + */ +static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, + unsigned int stream) +{ + int data_reg; + + if (mcbsp->pdata->reg_size == 2) { + if (stream) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DXR1; + } else { + if (stream) + data_reg = OMAP_MCBSP_REG_DRR; + else + data_reg = OMAP_MCBSP_REG_DXR; + } + + return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; +} + +static void omap_st_on(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + if (mcbsp->pdata->enable_st_clock) + mcbsp->pdata->enable_st_clock(mcbsp->id, 1); + + /* Enable McBSP Sidetone */ + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); + + /* Enable Sidetone from Sidetone Core */ + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); +} + +static void omap_st_off(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); + + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); + + if (mcbsp->pdata->enable_st_clock) + mcbsp->pdata->enable_st_clock(mcbsp->id, 0); +} + +static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) +{ + u16 val, i; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + + if (val & ST_COEFFWREN) + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); + + for (i = 0; i < 128; i++) + MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); + + i = 0; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + while (!(val & ST_COEFFWRDONE) && (++i < 1000)) + val = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + if (i == 1000) + dev_err(mcbsp->dev, "McBSP FIR load error!\n"); +} + +static void omap_st_chgain(struct omap_mcbsp *mcbsp) +{ + u16 w; + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \ + ST_CH1GAIN(st_data->ch1gain)); +} + +int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + st_data->ch0gain = chgain; + else if (channel == 1) + st_data->ch1gain = chgain; + else + ret = -EINVAL; + + if (st_data->enabled) + omap_st_chgain(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + *chgain = st_data->ch0gain; + else if (channel == 1) + *chgain = st_data->ch1gain; + else + ret = -EINVAL; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_st_start(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->enabled && !st_data->running) { + omap_st_fir_write(mcbsp, st_data->taps); + omap_st_chgain(mcbsp); + + if (!mcbsp->free) { + omap_st_on(mcbsp); + st_data->running = 1; + } + } + + return 0; +} + +int omap_st_enable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + st_data->enabled = 1; + omap_st_start(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return 0; +} + +static int omap_st_stop(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->running) { + if (!mcbsp->free) { + omap_st_off(mcbsp); + st_data->running = 0; + } + } + + return 0; +} + +int omap_st_disable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + omap_st_stop(mcbsp); + st_data->enabled = 0; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +int omap_st_is_enabled(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + return st_data->enabled; +} + +/* + * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH2 register. + */ +void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (mcbsp->pdata->buffer_size == 0) + return; + + if (threshold && threshold <= mcbsp->max_tx_thres) + MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); +} + +/* + * omap_mcbsp_set_rx_threshold configures the receive threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH1 register. + */ +void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (mcbsp->pdata->buffer_size == 0) + return; + + if (threshold && threshold <= mcbsp->max_rx_thres) + MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); +} + +/* + * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO + */ +u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat; + + if (mcbsp->pdata->buffer_size == 0) + return 0; + + /* Returns the number of free locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); + + /* Number of slots are different in McBSP ports */ + return mcbsp->pdata->buffer_size - buffstat; +} + +/* + * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO + * to reach the threshold value (when the DMA will be triggered to read it) + */ +u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat, threshold; + + if (mcbsp->pdata->buffer_size == 0) + return 0; + + /* Returns the number of used locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); + /* RX threshold */ + threshold = MCBSP_READ(mcbsp, THRSH1); + + /* Return the number of location till we reach the threshold limit */ + if (threshold <= buffstat) + return 0; + else + return threshold - buffstat; +} + +int omap_mcbsp_request(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + int err; + + reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); + if (!reg_cache) { + return -ENOMEM; + } + + spin_lock(&mcbsp->lock); + if (!mcbsp->free) { + dev_err(mcbsp->dev, "McBSP%d is currently in use\n", + mcbsp->id); + err = -EBUSY; + goto err_kfree; + } + + mcbsp->free = false; + mcbsp->reg_cache = reg_cache; + spin_unlock(&mcbsp->lock); + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) + mcbsp->pdata->ops->request(mcbsp->id - 1); + + /* + * Make sure that transmitter, receiver and sample-rate generator are + * not running before activating IRQs. + */ + MCBSP_WRITE(mcbsp, SPCR1, 0); + MCBSP_WRITE(mcbsp, SPCR2, 0); + + err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, + 0, "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request TX IRQ %d " + "for McBSP%d\n", mcbsp->tx_irq, + mcbsp->id); + goto err_clk_disable; + } + + if (mcbsp->rx_irq) { + err = request_irq(mcbsp->rx_irq, + omap_mcbsp_rx_irq_handler, + 0, "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request RX IRQ %d " + "for McBSP%d\n", mcbsp->rx_irq, + mcbsp->id); + goto err_free_irq; + } + } + + return 0; +err_free_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +err_clk_disable: + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + spin_lock(&mcbsp->lock); + mcbsp->free = true; + mcbsp->reg_cache = NULL; +err_kfree: + spin_unlock(&mcbsp->lock); + kfree(reg_cache); + + return err; +} + +void omap_mcbsp_free(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + if (mcbsp->rx_irq) + free_irq(mcbsp->rx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + + reg_cache = mcbsp->reg_cache; + + /* + * Select CLKS source from internal source unconditionally before + * marking the McBSP port as free. + * If the external clock source via MCBSP_CLKS pin has been selected the + * system will refuse to enter idle if the CLKS pin source is not reset + * back to internal source. + */ + if (!cpu_class_is_omap1()) + omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); + + spin_lock(&mcbsp->lock); + if (mcbsp->free) + dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); + else + mcbsp->free = true; + mcbsp->reg_cache = NULL; + spin_unlock(&mcbsp->lock); + + if (reg_cache) + kfree(reg_cache); +} + +/* + * Here we start the McBSP, by enabling transmitter, receiver or both. + * If no transmitter or receiver is active prior calling, then sample-rate + * generator and frame sync are started. + */ +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) +{ + int enable_srg = 0; + u16 w; + + if (mcbsp->st_data) + omap_st_start(mcbsp); + + /* Only enable SRG, if McBSP is master */ + w = MCBSP_READ_CACHE(mcbsp, PCR0); + if (w & (FSXM | FSRM | CLKXM | CLKRM)) + enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (enable_srg) { + /* Start the sample generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); + } + + /* Enable transmitter and receiver */ + tx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | tx); + + rx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w | rx); + + /* + * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec + * REVISIT: 100us may give enough time for two CLKSRG, however + * due to some unknown PM related, clock gating etc. reason it + * is now at 500us. + */ + udelay(500); + + if (enable_srg) { + /* Start frame sync */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); + } + + if (mcbsp->pdata->has_ccr) { + /* Release the transmitter and receiver */ + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w &= ~(tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w &= ~(rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(mcbsp); +} + +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx) +{ + int idle; + u16 w; + + /* Reset transmitter */ + tx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w |= (tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); + + /* Reset receiver */ + rx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w |= (rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); + + idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (idle) { + /* Reset the sample rate generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); + } + + if (mcbsp->st_data) + omap_st_stop(mcbsp); +} + +int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) +{ + const char *src; + + if (fck_src_id == MCBSP_CLKS_PAD_SRC) + src = "clks_ext"; + else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) + src = "clks_fclk"; + else + return -EINVAL; + + if (mcbsp->pdata->set_clk_src) + return mcbsp->pdata->set_clk_src(mcbsp->dev, mcbsp->fclk, src); + else + return -EINVAL; +} + +int omap_mcbsp_6pin_src_mux(struct omap_mcbsp *mcbsp, u8 mux) +{ + const char *signal, *src; + + if (mcbsp->pdata->mux_signal) + return -EINVAL; + + switch (mux) { + case CLKR_SRC_CLKR: + signal = "clkr"; + src = "clkr"; + break; + case CLKR_SRC_CLKX: + signal = "clkr"; + src = "clkx"; + break; + case FSR_SRC_FSR: + signal = "fsr"; + src = "fsr"; + break; + case FSR_SRC_FSX: + signal = "fsr"; + src = "fsx"; + break; + default: + return -EINVAL; + } + + return mcbsp->pdata->mux_signal(mcbsp->dev, signal, src); +} + +#define max_thres(m) (mcbsp->pdata->buffer_size) +#define valid_threshold(m, val) ((val) <= max_thres(m)) +#define THRESHOLD_PROP_BUILDER(prop) \ +static ssize_t prop##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%u\n", mcbsp->prop); \ +} \ + \ +static ssize_t prop##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + unsigned long val; \ + int status; \ + \ + status = strict_strtoul(buf, 0, &val); \ + if (status) \ + return status; \ + \ + if (!valid_threshold(mcbsp, val)) \ + return -EDOM; \ + \ + mcbsp->prop = val; \ + return size; \ +} \ + \ +static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store); + +THRESHOLD_PROP_BUILDER(max_tx_thres); +THRESHOLD_PROP_BUILDER(max_rx_thres); + +static const char *dma_op_modes[] = { + "element", "threshold", "frame", +}; + +static ssize_t dma_op_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int dma_op_mode, i = 0; + ssize_t len = 0; + const char * const *s; + + dma_op_mode = mcbsp->dma_op_mode; + + for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { + if (dma_op_mode == i) + len += sprintf(buf + len, "[%s] ", *s); + else + len += sprintf(buf + len, "%s ", *s); + } + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t dma_op_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + const char * const *s; + int i = 0; + + for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) + if (sysfs_streq(buf, *s)) + break; + + if (i == ARRAY_SIZE(dma_op_modes)) + return -EINVAL; + + spin_lock_irq(&mcbsp->lock); + if (!mcbsp->free) { + size = -EBUSY; + goto unlock; + } + mcbsp->dma_op_mode = i; + +unlock: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store); + +static const struct attribute *additional_attrs[] = { + &dev_attr_max_tx_thres.attr, + &dev_attr_max_rx_thres.attr, + &dev_attr_dma_op_mode.attr, + NULL, +}; + +static const struct attribute_group additional_attr_group = { + .attrs = (struct attribute **)additional_attrs, +}; + +static ssize_t st_taps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + ssize_t status = 0; + int i; + + spin_lock_irq(&mcbsp->lock); + for (i = 0; i < st_data->nr_taps; i++) + status += sprintf(&buf[status], (i ? ", %d" : "%d"), + st_data->taps[i]); + if (i) + status += sprintf(&buf[status], "\n"); + spin_unlock_irq(&mcbsp->lock); + + return status; +} + +static ssize_t st_taps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int val, tmp, status, i = 0; + + spin_lock_irq(&mcbsp->lock); + memset(st_data->taps, 0, sizeof(st_data->taps)); + st_data->nr_taps = 0; + + do { + status = sscanf(buf, "%d%n", &val, &tmp); + if (status < 0 || status == 0) { + size = -EINVAL; + goto out; + } + if (val < -32768 || val > 32767) { + size = -EINVAL; + goto out; + } + st_data->taps[i++] = val; + buf += tmp; + if (*buf != ',') + break; + buf++; + } while (1); + + st_data->nr_taps = i; + +out: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store); + +static const struct attribute *sidetone_attrs[] = { + &dev_attr_st_taps.attr, + NULL, +}; + +static const struct attribute_group sidetone_attr_group = { + .attrs = (struct attribute **)sidetone_attrs, +}; + +static int __devinit omap_st_add(struct omap_mcbsp *mcbsp, + struct resource *res) +{ + struct omap_mcbsp_st_data *st_data; + int err; + + st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); + if (!st_data) + return -ENOMEM; + + st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, + resource_size(res)); + if (!st_data->io_base_st) + return -ENOMEM; + + err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); + if (err) + return err; + + mcbsp->st_data = st_data; + return 0; +} + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +int __devinit omap_mcbsp_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct resource *res; + int ret = 0; + + spin_lock_init(&mcbsp->lock); + mcbsp->free = true; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!res) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(mcbsp->dev, "invalid memory resource\n"); + return -ENOMEM; + } + } + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + dev_name(&pdev->dev))) { + dev_err(mcbsp->dev, "memory region already claimed\n"); + return -ENODEV; + } + + mcbsp->phys_base = res->start; + mcbsp->reg_cache_size = resource_size(res); + mcbsp->io_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!mcbsp->io_base) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) + mcbsp->phys_dma_base = mcbsp->phys_base; + else + mcbsp->phys_dma_base = res->start; + + mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); + mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); + + /* From OMAP4 there will be a single irq line */ + if (mcbsp->tx_irq == -ENXIO) { + mcbsp->tx_irq = platform_get_irq(pdev, 0); + mcbsp->rx_irq = 0; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!res) { + dev_err(&pdev->dev, "invalid rx DMA channel\n"); + return -ENODEV; + } + /* RX DMA request number, and port address configuration */ + mcbsp->dma_data[1].name = "Audio Capture"; + mcbsp->dma_data[1].dma_req = res->start; + mcbsp->dma_data[1].port_addr = omap_mcbsp_dma_reg_params(mcbsp, 1); + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!res) { + dev_err(&pdev->dev, "invalid tx DMA channel\n"); + return -ENODEV; + } + /* TX DMA request number, and port address configuration */ + mcbsp->dma_data[0].name = "Audio Playback"; + mcbsp->dma_data[0].dma_req = res->start; + mcbsp->dma_data[0].port_addr = omap_mcbsp_dma_reg_params(mcbsp, 0); + + mcbsp->fclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(mcbsp->fclk)) { + ret = PTR_ERR(mcbsp->fclk); + dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); + return ret; + } + + mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; + if (mcbsp->pdata->buffer_size) { + /* + * Initially configure the maximum thresholds to a safe value. + * The McBSP FIFO usage with these values should not go under + * 16 locations. + * If the whole FIFO without safety buffer is used, than there + * is a possibility that the DMA will be not able to push the + * new data on time, causing channel shifts in runtime. + */ + mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; + mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; + + ret = sysfs_create_group(&mcbsp->dev->kobj, + &additional_attr_group); + if (ret) { + dev_err(mcbsp->dev, + "Unable to create additional controls\n"); + goto err_thres; + } + } else { + mcbsp->max_tx_thres = -EINVAL; + mcbsp->max_rx_thres = -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); + if (res) { + ret = omap_st_add(mcbsp, res); + if (ret) { + dev_err(mcbsp->dev, + "Unable to create sidetone controls\n"); + goto err_st; + } + } + + return 0; + +err_st: + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); +err_thres: + clk_put(mcbsp->fclk); + return ret; +} + +void __devexit omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp) +{ + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); + + if (mcbsp->st_data) + sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); +} diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h new file mode 100644 index 000000000000..a944fcc9073c --- /dev/null +++ b/sound/soc/omap/mcbsp.h @@ -0,0 +1,346 @@ +/* + * sound/soc/omap/mcbsp.h + * + * OMAP Multi-Channel Buffered Serial Port + * + * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> + * Peter Ujfalusi <peter.ujfalusi@ti.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 + * + */ +#ifndef __ASOC_MCBSP_H +#define __ASOC_MCBSP_H + +#include "omap-pcm.h" + +/* McBSP register numbers. Register address offset = num * reg_step */ +enum { + /* Common registers */ + OMAP_MCBSP_REG_SPCR2 = 4, + OMAP_MCBSP_REG_SPCR1, + OMAP_MCBSP_REG_RCR2, + OMAP_MCBSP_REG_RCR1, + OMAP_MCBSP_REG_XCR2, + OMAP_MCBSP_REG_XCR1, + OMAP_MCBSP_REG_SRGR2, + OMAP_MCBSP_REG_SRGR1, + OMAP_MCBSP_REG_MCR2, + OMAP_MCBSP_REG_MCR1, + OMAP_MCBSP_REG_RCERA, + OMAP_MCBSP_REG_RCERB, + OMAP_MCBSP_REG_XCERA, + OMAP_MCBSP_REG_XCERB, + OMAP_MCBSP_REG_PCR0, + OMAP_MCBSP_REG_RCERC, + OMAP_MCBSP_REG_RCERD, + OMAP_MCBSP_REG_XCERC, + OMAP_MCBSP_REG_XCERD, + OMAP_MCBSP_REG_RCERE, + OMAP_MCBSP_REG_RCERF, + OMAP_MCBSP_REG_XCERE, + OMAP_MCBSP_REG_XCERF, + OMAP_MCBSP_REG_RCERG, + OMAP_MCBSP_REG_RCERH, + OMAP_MCBSP_REG_XCERG, + OMAP_MCBSP_REG_XCERH, + + /* OMAP1-OMAP2420 registers */ + OMAP_MCBSP_REG_DRR2 = 0, + OMAP_MCBSP_REG_DRR1, + OMAP_MCBSP_REG_DXR2, + OMAP_MCBSP_REG_DXR1, + + /* OMAP2430 and onwards */ + OMAP_MCBSP_REG_DRR = 0, + OMAP_MCBSP_REG_DXR = 2, + OMAP_MCBSP_REG_SYSCON = 35, + OMAP_MCBSP_REG_THRSH2, + OMAP_MCBSP_REG_THRSH1, + OMAP_MCBSP_REG_IRQST = 40, + OMAP_MCBSP_REG_IRQEN, + OMAP_MCBSP_REG_WAKEUPEN, + OMAP_MCBSP_REG_XCCR, + OMAP_MCBSP_REG_RCCR, + OMAP_MCBSP_REG_XBUFFSTAT, + OMAP_MCBSP_REG_RBUFFSTAT, + OMAP_MCBSP_REG_SSELCR, +}; + +/* OMAP3 sidetone control registers */ +#define OMAP_ST_REG_REV 0x00 +#define OMAP_ST_REG_SYSCONFIG 0x10 +#define OMAP_ST_REG_IRQSTATUS 0x18 +#define OMAP_ST_REG_IRQENABLE 0x1C +#define OMAP_ST_REG_SGAINCR 0x24 +#define OMAP_ST_REG_SFIRCR 0x28 +#define OMAP_ST_REG_SSELCR 0x2C + +/************************** McBSP SPCR1 bit definitions ***********************/ +#define RRST BIT(0) +#define RRDY BIT(1) +#define RFULL BIT(2) +#define RSYNC_ERR BIT(3) +#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define ABIS BIT(6) +#define DXENA BIT(7) +#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */ +#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */ +#define ALB BIT(15) +#define DLB BIT(15) + +/************************** McBSP SPCR2 bit definitions ***********************/ +#define XRST BIT(0) +#define XRDY BIT(1) +#define XEMPTY BIT(2) +#define XSYNC_ERR BIT(3) +#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define GRST BIT(6) +#define FRST BIT(7) +#define SOFT BIT(8) +#define FREE BIT(9) + +/************************** McBSP PCR bit definitions *************************/ +#define CLKRP BIT(0) +#define CLKXP BIT(1) +#define FSRP BIT(2) +#define FSXP BIT(3) +#define DR_STAT BIT(4) +#define DX_STAT BIT(5) +#define CLKS_STAT BIT(6) +#define SCLKME BIT(7) +#define CLKRM BIT(8) +#define CLKXM BIT(9) +#define FSRM BIT(10) +#define FSXM BIT(11) +#define RIOEN BIT(12) +#define XIOEN BIT(13) +#define IDLE_EN BIT(14) + +/************************** McBSP RCR1 bit definitions ************************/ +#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/************************** McBSP XCR1 bit definitions ************************/ +#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/*************************** McBSP RCR2 bit definitions ***********************/ +#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define RFIG BIT(2) +#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define RPHASE BIT(15) + +/*************************** McBSP XCR2 bit definitions ***********************/ +#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define XFIG BIT(2) +#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define XPHASE BIT(15) + +/************************* McBSP SRGR1 bit definitions ************************/ +#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */ +#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */ + +/************************* McBSP SRGR2 bit definitions ************************/ +#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */ +#define FSGM BIT(12) +#define CLKSM BIT(13) +#define CLKSP BIT(14) +#define GSYNC BIT(15) + +/************************* McBSP MCR1 bit definitions *************************/ +#define RMCM BIT(0) +#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/************************* McBSP MCR2 bit definitions *************************/ +#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */ +#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/*********************** McBSP XCCR bit definitions *************************/ +#define XDISABLE BIT(0) +#define XDMAEN BIT(3) +#define DILB BIT(5) +#define XFULL_CYCLE BIT(11) +#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */ +#define PPCONNECT BIT(14) +#define EXTCLKGATE BIT(15) + +/********************** McBSP RCCR bit definitions *************************/ +#define RDISABLE BIT(0) +#define RDMAEN BIT(3) +#define RFULL_CYCLE BIT(11) + +/********************** McBSP SYSCONFIG bit definitions ********************/ +#define SOFTRST BIT(1) +#define ENAWAKEUP BIT(2) +#define SIDLEMODE(value) (((value) & 0x3) << 3) +#define CLOCKACTIVITY(value) (((value) & 0x3) << 8) + +/********************** McBSP SSELCR bit definitions ***********************/ +#define SIDETONEEN BIT(10) + +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ +#define ST_AUTOIDLE BIT(0) + +/********************** McBSP Sidetone SGAINCR bit definitions *************/ +#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ +#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ + +/********************** McBSP Sidetone SFIRCR bit definitions **************/ +#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ + +/********************** McBSP Sidetone SSELCR bit definitions **************/ +#define ST_SIDETONEEN BIT(0) +#define ST_COEFFWREN BIT(1) +#define ST_COEFFWRDONE BIT(2) + +/********************** McBSP DMA operating modes **************************/ +#define MCBSP_DMA_MODE_ELEMENT 0 +#define MCBSP_DMA_MODE_THRESHOLD 1 +#define MCBSP_DMA_MODE_FRAME 2 + +/********************** McBSP WAKEUPEN bit definitions *********************/ +#define RSYNCERREN BIT(0) +#define RFSREN BIT(1) +#define REOFEN BIT(2) +#define RRDYEN BIT(3) +#define XSYNCERREN BIT(7) +#define XFSXEN BIT(8) +#define XEOFEN BIT(9) +#define XRDYEN BIT(10) +#define XEMPTYEOFEN BIT(14) + +/* Clock signal muxing options */ +#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */ +#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */ +#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */ +#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */ + +/* McBSP functional clock sources */ +#define MCBSP_CLKS_PRCM_SRC 0 +#define MCBSP_CLKS_PAD_SRC 1 + +/* we don't do multichannel for now */ +struct omap_mcbsp_reg_cfg { + u16 spcr2; + u16 spcr1; + u16 rcr2; + u16 rcr1; + u16 xcr2; + u16 xcr1; + u16 srgr2; + u16 srgr1; + u16 mcr2; + u16 mcr1; + u16 pcr0; + u16 rcerc; + u16 rcerd; + u16 xcerc; + u16 xcerd; + u16 rcere; + u16 rcerf; + u16 xcere; + u16 xcerf; + u16 rcerg; + u16 rcerh; + u16 xcerg; + u16 xcerh; + u16 xccr; + u16 rccr; +}; + +struct omap_mcbsp_st_data { + void __iomem *io_base_st; + bool running; + bool enabled; + s16 taps[128]; /* Sidetone filter coefficients */ + int nr_taps; /* Number of filter coefficients in use */ + s16 ch0gain; + s16 ch1gain; +}; + +struct omap_mcbsp { + struct device *dev; + struct clk *fclk; + spinlock_t lock; + unsigned long phys_base; + unsigned long phys_dma_base; + void __iomem *io_base; + u8 id; + /* + * Flags indicating is the bus already activated and configured by + * another substream + */ + int active; + int configured; + u8 free; + + int rx_irq; + int tx_irq; + + /* Protect the field .free, while checking if the mcbsp is in use */ + struct omap_mcbsp_platform_data *pdata; + struct omap_mcbsp_st_data *st_data; + struct omap_mcbsp_reg_cfg cfg_regs; + struct omap_pcm_dma_data dma_data[2]; + int dma_op_mode; + u16 max_tx_thres; + u16 max_rx_thres; + void *reg_cache; + int reg_cache_size; + + unsigned int fmt; + unsigned int in_freq; + int clk_div; + int wlen; +}; + +void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config); +void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); +void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); +u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp); +u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); +int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); +int omap_mcbsp_request(struct omap_mcbsp *mcbsp); +void omap_mcbsp_free(struct omap_mcbsp *mcbsp); +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx); +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx); + +/* McBSP functional clock source changing function */ +int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); + +/* McBSP signal muxing API */ +int omap_mcbsp_6pin_src_mux(struct omap_mcbsp *mcbsp, u8 mux); + +/* Sidetone specific API */ +int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain); +int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain); +int omap_st_enable(struct omap_mcbsp *mcbsp); +int omap_st_disable(struct omap_mcbsp *mcbsp); +int omap_st_is_enabled(struct omap_mcbsp *mcbsp); + +int __devinit omap_mcbsp_init(struct platform_device *pdev); +void __devexit omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp); + +#endif /* __ASOC_MCBSP_H */ diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index c292bf0fd19c..abac4b690750 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -275,7 +275,7 @@ static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link n810_dai = { .name = "TLV320AIC33", .stream_name = "AIC33", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .platform_name = "omap-pcm-audio", .codec_name = "tlv320aic3x-codec.2-0018", .codec_dai_name = "tlv320aic3x-hifi", diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 1287b870f221..6912ac7cb625 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -25,6 +25,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -33,6 +34,7 @@ #include <plat/dma.h> #include <plat/mcbsp.h> +#include "mcbsp.h" #include "omap-mcbsp.h" #include "omap-pcm.h" @@ -46,42 +48,31 @@ .private_value = (unsigned long) &(struct soc_mixer_control) \ {.min = xmin, .max = xmax} } -struct omap_mcbsp_data { - unsigned int bus_id; - struct omap_mcbsp_reg_cfg regs; - unsigned int fmt; - /* - * Flags indicating is the bus already activated and configured by - * another substream - */ - int active; - int configured; - unsigned int in_freq; - int clk_div; - int wlen; +enum { + OMAP_MCBSP_WORD_8 = 0, + OMAP_MCBSP_WORD_12, + OMAP_MCBSP_WORD_16, + OMAP_MCBSP_WORD_20, + OMAP_MCBSP_WORD_24, + OMAP_MCBSP_WORD_32, }; -static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; - /* * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs */ -static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; - static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); struct omap_pcm_dma_data *dma_data; - int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); int words; dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ - if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) + if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) /* * Configure McBSP threshold based on either: * packet_size, when the sDMA is in packet mode, or @@ -91,15 +82,15 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) words = dma_data->packet_size; else words = snd_pcm_lib_period_bytes(substream) / - (mcbsp_data->wlen / 8); + (mcbsp->wlen / 8); else words = 1; /* Configure McBSP internal buffer usage */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, words); + omap_mcbsp_set_tx_threshold(mcbsp, words); else - omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, words); + omap_mcbsp_set_rx_threshold(mcbsp, words); } static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, @@ -109,12 +100,12 @@ static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct omap_mcbsp_data *mcbsp_data = rule->private; + struct omap_mcbsp *mcbsp = rule->private; struct snd_interval frames; int size; snd_interval_any(&frames); - size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id); + size = mcbsp->pdata->buffer_size; frames.min = size / channels->min; frames.integer = 1; @@ -124,12 +115,11 @@ static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); - int bus_id = mcbsp_data->bus_id; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int err = 0; if (!cpu_dai->active) - err = omap_mcbsp_request(bus_id); + err = omap_mcbsp_request(mcbsp); /* * OMAP3 McBSP FIFO is word structured. @@ -146,16 +136,16 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) */ - if (cpu_is_omap34xx() || cpu_is_omap44xx()) { + if (mcbsp->pdata->buffer_size) { /* * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns */ snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, omap_mcbsp_hwrule_min_buffersize, - mcbsp_data, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + mcbsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, @@ -168,33 +158,33 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); if (!cpu_dai->active) { - omap_mcbsp_free(mcbsp_data->bus_id); - mcbsp_data->configured = 0; + omap_mcbsp_free(mcbsp); + mcbsp->configured = 0; } } static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - mcbsp_data->active++; - omap_mcbsp_start(mcbsp_data->bus_id, play, !play); + mcbsp->active++; + omap_mcbsp_start(mcbsp, play, !play); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - omap_mcbsp_stop(mcbsp_data->bus_id, play, !play); - mcbsp_data->active--; + omap_mcbsp_stop(mcbsp, play, !play); + mcbsp->active--; break; default: err = -EINVAL; @@ -209,14 +199,14 @@ static snd_pcm_sframes_t omap_mcbsp_dai_delay( { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); u16 fifo_use; snd_pcm_sframes_t delay; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fifo_use = omap_mcbsp_get_tx_delay(mcbsp_data->bus_id); + fifo_use = omap_mcbsp_get_tx_delay(mcbsp); else - fifo_use = omap_mcbsp_get_rx_delay(mcbsp_data->bus_id); + fifo_use = omap_mcbsp_get_rx_delay(mcbsp); /* * Divide the used locations with the channel count to get the @@ -232,19 +222,14 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; struct omap_pcm_dma_data *dma_data; - int dma, bus_id = mcbsp_data->bus_id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; int pkt_size = 0; - unsigned long port; unsigned int format, div, framesize, master; - dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; - - dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream); - port = omap_mcbsp_dma_reg_params(bus_id, substream->stream); + dma_data = &mcbsp->dma_data[substream->stream]; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -258,20 +243,17 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } - if (cpu_is_omap34xx() || cpu_is_omap44xx()) { + if (mcbsp->pdata->buffer_size) { dma_data->set_threshold = omap_mcbsp_set_threshold; /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ - if (omap_mcbsp_get_dma_op_mode(bus_id) == - MCBSP_DMA_MODE_THRESHOLD) { + if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - max_thrsh = omap_mcbsp_get_max_tx_threshold( - mcbsp_data->bus_id); + max_thrsh = mcbsp->max_tx_thres; else - max_thrsh = omap_mcbsp_get_max_rx_threshold( - mcbsp_data->bus_id); + max_thrsh = mcbsp->max_rx_thres; /* * If the period contains less or equal number of words, * we are using the original threshold mode setup: @@ -304,15 +286,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } } - dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; - dma_data->dma_req = dma; - dma_data->port_addr = port; dma_data->sync_mode = sync_mode; dma_data->packet_size = pkt_size; snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - if (mcbsp_data->configured) { + if (mcbsp->configured) { /* McBSP already configured by another stream */ return 0; } @@ -321,7 +300,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); - format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || format == SND_SOC_DAIFMT_LEFT_J)) { @@ -359,10 +338,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, /* In McBSP master modes, FRAME (i.e. sample rate) is generated * by _counting_ BCLKs. Calculate frame size in BCLKs */ - master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; + master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK; if (master == SND_SOC_DAIFMT_CBS_CFS) { - div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; - framesize = (mcbsp_data->in_freq / div) / params_rate(params); + div = mcbsp->clk_div ? mcbsp->clk_div : 1; + framesize = (mcbsp->in_freq / div) / params_rate(params); if (framesize < wlen * channels) { printk(KERN_ERR "%s: not enough bandwidth for desired rate and " @@ -388,9 +367,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, break; } - omap_mcbsp_config(bus_id, &mcbsp_data->regs); - mcbsp_data->wlen = wlen; - mcbsp_data->configured = 1; + omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs); + mcbsp->wlen = wlen; + mcbsp->configured = 1; return 0; } @@ -402,14 +381,14 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; bool inv_fs = false; - if (mcbsp_data->configured) + if (mcbsp->configured) return 0; - mcbsp_data->fmt = fmt; + mcbsp->fmt = fmt; memset(regs, 0, sizeof(*regs)); /* Generic McBSP register settings */ regs->spcr2 |= XINTM(3) | FREE; @@ -504,13 +483,13 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; if (div_id != OMAP_MCBSP_CLKGDV) return -ENODEV; - mcbsp_data->clk_div = div; + mcbsp->clk_div = div; regs->srgr1 &= ~CLKGDV(0xff); regs->srgr1 |= CLKGDV(div - 1); @@ -521,28 +500,32 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; int err = 0; - if (mcbsp_data->active) { - if (freq == mcbsp_data->in_freq) + if (mcbsp->active) { + if (freq == mcbsp->in_freq) return 0; else return -EBUSY; } - /* The McBSP signal muxing functions are only available on McBSP1 */ - if (clk_id == OMAP_MCBSP_CLKR_SRC_CLKR || - clk_id == OMAP_MCBSP_CLKR_SRC_CLKX || - clk_id == OMAP_MCBSP_FSR_SRC_FSR || - clk_id == OMAP_MCBSP_FSR_SRC_FSX) - if (cpu_class_is_omap1() || mcbsp_data->bus_id != 0) - return -EINVAL; - - mcbsp_data->in_freq = freq; - regs->srgr2 &= ~CLKSM; - regs->pcr0 &= ~SCLKME; + if (clk_id == OMAP_MCBSP_SYSCLK_CLK || + clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK || + clk_id == OMAP_MCBSP_SYSCLK_CLKS_EXT || + clk_id == OMAP_MCBSP_SYSCLK_CLKX_EXT || + clk_id == OMAP_MCBSP_SYSCLK_CLKR_EXT) { + mcbsp->in_freq = freq; + regs->srgr2 &= ~CLKSM; + regs->pcr0 &= ~SCLKME; + } else if (cpu_class_is_omap1()) { + /* + * McBSP CLKR/FSR signal muxing functions are only available on + * OMAP2 or newer versions + */ + return -EINVAL; + } switch (clk_id) { case OMAP_MCBSP_SYSCLK_CLK: @@ -553,7 +536,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, err = -EINVAL; break; } - err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id, + err = omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); break; case OMAP_MCBSP_SYSCLK_CLKS_EXT: @@ -561,7 +544,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, err = 0; break; } - err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id, + err = omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PAD_SRC); break; @@ -573,24 +556,16 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, case OMAP_MCBSP_CLKR_SRC_CLKR: - if (cpu_class_is_omap1()) - break; - omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKR); + err = omap_mcbsp_6pin_src_mux(mcbsp, CLKR_SRC_CLKR); break; case OMAP_MCBSP_CLKR_SRC_CLKX: - if (cpu_class_is_omap1()) - break; - omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKX); + err = omap_mcbsp_6pin_src_mux(mcbsp, CLKR_SRC_CLKX); break; case OMAP_MCBSP_FSR_SRC_FSR: - if (cpu_class_is_omap1()) - break; - omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSR); + err = omap_mcbsp_6pin_src_mux(mcbsp, FSR_SRC_FSR); break; case OMAP_MCBSP_FSR_SRC_FSX: - if (cpu_class_is_omap1()) - break; - omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSX); + err = omap_mcbsp_6pin_src_mux(mcbsp, FSR_SRC_FSX); break; default: err = -ENODEV; @@ -610,15 +585,27 @@ static const struct snd_soc_dai_ops mcbsp_dai_ops = { .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, }; -static int mcbsp_dai_probe(struct snd_soc_dai *dai) +static int omap_mcbsp_probe(struct snd_soc_dai *dai) { - mcbsp_data[dai->id].bus_id = dai->id; - snd_soc_dai_set_drvdata(dai, &mcbsp_data[dai->id].bus_id); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); + + pm_runtime_enable(mcbsp->dev); + + return 0; +} + +static int omap_mcbsp_remove(struct snd_soc_dai *dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); + + pm_runtime_disable(mcbsp->dev); + return 0; } static struct snd_soc_dai_driver omap_mcbsp_dai = { - .probe = mcbsp_dai_probe, + .probe = omap_mcbsp_probe, + .remove = omap_mcbsp_remove, .playback = { .channels_min = 1, .channels_max = 16, @@ -649,11 +636,13 @@ static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, return 0; } -#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \ +#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(channel) \ static int \ -omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ +omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ struct snd_ctl_elem_value *uc) \ { \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ struct soc_mixer_control *mc = \ (struct soc_mixer_control *)kc->private_value; \ int max = mc->max; \ @@ -664,46 +653,44 @@ omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ return -EINVAL; \ \ /* OMAP McBSP implementation uses index values 0..4 */ \ - return omap_st_set_chgain((id)-1, channel, val); \ + return omap_st_set_chgain(mcbsp, channel, val); \ } -#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \ +#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(channel) \ static int \ -omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ +omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ struct snd_ctl_elem_value *uc) \ { \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ s16 chgain; \ \ - if (omap_st_get_chgain((id)-1, channel, &chgain)) \ + if (omap_st_get_chgain(mcbsp, channel, &chgain)) \ return -EAGAIN; \ \ uc->value.integer.value[0] = chgain; \ return 0; \ } -OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0) -OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1) -OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0) -OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1) -OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0) -OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1) -OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0) -OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1) +OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(1) +OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(1) static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); u8 value = ucontrol->value.integer.value[0]; - if (value == omap_st_is_enabled(mc->reg)) + if (value == omap_st_is_enabled(mcbsp)) return 0; if (value) - omap_st_enable(mc->reg); + omap_st_enable(mcbsp); else - omap_st_disable(mc->reg); + omap_st_disable(mcbsp); return 1; } @@ -711,10 +698,10 @@ static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - ucontrol->value.integer.value[0] = omap_st_is_enabled(mc->reg); + ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp); return 0; } @@ -723,12 +710,12 @@ static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = { omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume", -32768, 32767, - omap_mcbsp2_get_st_ch0_volume, - omap_mcbsp2_set_st_ch0_volume), + omap_mcbsp_get_st_ch0_volume, + omap_mcbsp_set_st_ch0_volume), OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume", -32768, 32767, - omap_mcbsp2_get_st_ch1_volume, - omap_mcbsp2_set_st_ch1_volume), + omap_mcbsp_get_st_ch1_volume, + omap_mcbsp_set_st_ch1_volume), }; static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = { @@ -736,25 +723,30 @@ static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = { omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume", -32768, 32767, - omap_mcbsp3_get_st_ch0_volume, - omap_mcbsp3_set_st_ch0_volume), + omap_mcbsp_get_st_ch0_volume, + omap_mcbsp_set_st_ch0_volume), OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume", -32768, 32767, - omap_mcbsp3_get_st_ch1_volume, - omap_mcbsp3_set_st_ch1_volume), + omap_mcbsp_get_st_ch1_volume, + omap_mcbsp_set_st_ch1_volume), }; -int omap_mcbsp_st_add_controls(struct snd_soc_dai *dai) +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd) { - if (!cpu_is_omap34xx()) + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + if (!mcbsp->st_data) return -ENODEV; - switch (dai->id) { - case 1: /* McBSP 2 */ - return snd_soc_add_dai_controls(dai, omap_mcbsp2_st_controls, + switch (cpu_dai->id) { + case 2: /* McBSP 2 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp2_st_controls, ARRAY_SIZE(omap_mcbsp2_st_controls)); - case 2: /* McBSP 3 */ - return snd_soc_add_dai_controls(dai, omap_mcbsp3_st_controls, + case 3: /* McBSP 3 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp3_st_controls, ARRAY_SIZE(omap_mcbsp3_st_controls)); default: break; @@ -766,18 +758,51 @@ EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); static __devinit int asoc_mcbsp_probe(struct platform_device *pdev) { - return snd_soc_register_dai(&pdev->dev, &omap_mcbsp_dai); + struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct omap_mcbsp *mcbsp; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL); + if (!mcbsp) + return -ENOMEM; + + mcbsp->id = pdev->id; + mcbsp->pdata = pdata; + mcbsp->dev = &pdev->dev; + platform_set_drvdata(pdev, mcbsp); + + ret = omap_mcbsp_init(pdev); + if (!ret) + return snd_soc_register_dai(&pdev->dev, &omap_mcbsp_dai); + + return ret; } static int __devexit asoc_mcbsp_remove(struct platform_device *pdev) { + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + snd_soc_unregister_dai(&pdev->dev); + + if (mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id); + + omap_mcbsp_sysfs_remove(mcbsp); + + clk_put(mcbsp->fclk); + + platform_set_drvdata(pdev, NULL); + return 0; } static struct platform_driver asoc_mcbsp_driver = { .driver = { - .name = "omap-mcbsp-dai", + .name = "omap-mcbsp", .owner = THIS_MODULE, }, diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 476fe2add703..f877b16f19c9 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -59,6 +59,6 @@ enum omap_mcbsp_div { #define NUM_LINKS 5 #endif -int omap_mcbsp_st_add_controls(struct snd_soc_dai *dai); +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd); #endif diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index f95fe3064172..b92248cbd47a 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h @@ -25,6 +25,8 @@ #ifndef __OMAP_PCM_H__ #define __OMAP_PCM_H__ +struct snd_pcm_substream; + struct omap_pcm_dma_data { char *name; /* stream identifier */ int dma_req; /* DMA request line */ diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index 3357dcc47ed4..2830dfd05661 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c @@ -91,7 +91,7 @@ static struct snd_soc_ops omap3beagle_ops = { static struct snd_soc_dai_link omap3beagle_dai = { .name = "TWL4030", .stream_name = "TWL4030", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .platform_name = "omap-pcm-audio", .codec_dai_name = "twl4030-hifi", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c index 071fcb09b8b2..3d468c9179d7 100644 --- a/sound/soc/omap/omap3evm.c +++ b/sound/soc/omap/omap3evm.c @@ -58,7 +58,7 @@ static struct snd_soc_ops omap3evm_ops = { static struct snd_soc_dai_link omap3evm_dai = { .name = "TWL4030", .stream_name = "TWL4030", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 07794bd10952..4c3a0978578a 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -208,7 +208,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = { { .name = "PCM1773", .stream_name = "HiFi Out", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", @@ -219,7 +219,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = { }, { .name = "TWL4030", .stream_name = "Line/Mic In", - .cpu_dai_name = "omap-mcbsp-dai.3", + .cpu_dai_name = "omap-mcbsp.4", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c index d859b597e7ec..b1a9d64cbc56 100644 --- a/sound/soc/omap/osk5912.c +++ b/sound/soc/omap/osk5912.c @@ -96,7 +96,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static struct snd_soc_dai_link osk_dai = { .name = "TLV320AIC23", .stream_name = "AIC23", - .cpu_dai_name = "omap-mcbsp-dai.0", + .cpu_dai_name = "omap-mcbsp.1", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "omap-pcm-audio", .codec_name = "tlv320aic23-codec", diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c index 2ee889c50256..6ac3e0c3c282 100644 --- a/sound/soc/omap/overo.c +++ b/sound/soc/omap/overo.c @@ -60,7 +60,7 @@ static struct snd_soc_ops overo_ops = { static struct snd_soc_dai_link overo_dai = { .name = "TWL4030", .stream_name = "TWL4030", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 58936c730a87..2712dd232b6d 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -313,7 +313,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) return err; snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); - err = omap_mcbsp_st_add_controls(rtd->cpu_dai); + err = omap_mcbsp_st_add_controls(rtd); if (err < 0) return err; @@ -353,7 +353,7 @@ static struct snd_soc_dai_link rx51_dai[] = { { .name = "TLV320AIC34", .stream_name = "AIC34", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "tlv320aic3x-hifi", .platform_name = "omap-pcm-audio", .codec_name = "tlv320aic3x-codec.2-0018", diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 2c850662ea7e..0e283226e2bf 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -187,7 +187,7 @@ static struct snd_soc_dai_link sdp3430_dai[] = { { .name = "TWL4030 I2S", .stream_name = "TWL4030 Audio", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", @@ -199,7 +199,7 @@ static struct snd_soc_dai_link sdp3430_dai[] = { { .name = "TWL4030 PCM", .stream_name = "TWL4030 Voice", - .cpu_dai_name = "omap-mcbsp-dai.2", + .cpu_dai_name = "omap-mcbsp.3", .codec_dai_name = "twl4030-voice", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 981616d61f67..920e0d9e03db 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -131,7 +131,7 @@ static struct snd_soc_dai_link zoom2_dai[] = { { .name = "TWL4030 I2S", .stream_name = "TWL4030 Audio", - .cpu_dai_name = "omap-mcbsp-dai.1", + .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", @@ -143,7 +143,7 @@ static struct snd_soc_dai_link zoom2_dai[] = { { .name = "TWL4030 PCM", .stream_name = "TWL4030 Voice", - .cpu_dai_name = "omap-mcbsp-dai.2", + .cpu_dai_name = "omap-mcbsp.3", .codec_dai_name = "twl4030-voice", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index f3417f2311b8..fe3995ce9b38 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,8 +1,8 @@ config SND_SOC_SAMSUNG tristate "ASoC support for Samsung" - depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_EXYNOS4 + depends on ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_EXYNOS4 select S3C64XX_DMA if ARCH_S3C64XX - select S3C2410_DMA if ARCH_S3C2410 + select S3C2410_DMA if ARCH_S3C24XX help Say Y or M if you want to add support for codecs attached to the Samsung SoCs' Audio interfaces. You will also need to @@ -84,7 +84,7 @@ config SND_SOC_SAMSUNG_SMDK2443_WM9710 config SND_SOC_SAMSUNG_LN2440SBC_ALC650 tristate "SoC AC97 Audio support for LN2440SBC - ALC650" - depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + depends on SND_SOC_SAMSUNG && ARCH_S3C24XX select S3C2410_DMA select AC97_BUS select SND_SOC_AC97_CODEC @@ -95,7 +95,7 @@ config SND_SOC_SAMSUNG_LN2440SBC_ALC650 config SND_SOC_SAMSUNG_S3C24XX_UDA134X tristate "SoC I2S Audio support UDA134X wired to a S3C24XX" - depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + depends on SND_SOC_SAMSUNG && ARCH_S3C24XX select SND_S3C24XX_I2S select SND_SOC_L3 select SND_SOC_UDA134X @@ -107,14 +107,14 @@ config SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23 tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" - depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + depends on SND_SOC_SAMSUNG && ARCH_S3C24XX select SND_S3C24XX_I2S select SND_SOC_TLV320AIC23 select SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_SIMTEC_HERMES tristate "SoC I2S Audio support for Simtec Hermes board" - depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + depends on SND_SOC_SAMSUNG && ARCH_S3C24XX select SND_S3C24XX_I2S select SND_SOC_TLV320AIC3X select SND_SOC_SAMSUNG_SIMTEC diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 4dd051bdf4fd..c6500d00053b 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -1112,17 +1112,7 @@ static struct spi_driver at73c213_driver = { .remove = __devexit_p(snd_at73c213_remove), }; -static int __init at73c213_init(void) -{ - return spi_register_driver(&at73c213_driver); -} -module_init(at73c213_init); - -static void __exit at73c213_exit(void) -{ - spi_unregister_driver(&at73c213_driver); -} -module_exit(at73c213_exit); +module_spi_driver(at73c213_driver); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC"); diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 8af92e3e9c18..fc8cc823e438 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -29,7 +28,7 @@ #include <sound/initval.h> MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>"); -MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0"); +MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver"); MODULE_LICENSE("GPL v2"); MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}"); diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h index d11e5cb520f0..bde02d105a51 100644 --- a/sound/usb/6fire/chip.h +++ b/sound/usb/6fire/chip.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index c994daa57af2..6c3d531a250e 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h index edc5dc84b888..d2af0a5ddcf3 100644 --- a/sound/usb/6fire/comm.h +++ b/sound/usb/6fire/comm.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h index 7dbeb4a37831..b6eb03ed1c2c 100644 --- a/sound/usb/6fire/common.h +++ b/sound/usb/6fire/common.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index ac828eff1a63..07ed914d5e71 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -5,9 +5,12 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * + * Thanks to: + * - Holger Ruckdeschel: he found out how to control individual channel + * volumes and introduced mute switch + * * 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 @@ -16,6 +19,7 @@ #include <linux/interrupt.h> #include <sound/control.h> +#include <sound/tlv.h> #include "control.h" #include "comm.h" @@ -25,26 +29,6 @@ static char *opt_coax_texts[2] = { "Optical", "Coax" }; static char *line_phono_texts[2] = { "Line", "Phono" }; /* - * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$ - * this is done because the linear values cause rapid degredation - * of volume in the uppermost region. - */ -static const u8 log_volume_table[128] = { - 0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34, - 0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43, - 0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, - 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, - 0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, - 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, - 0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, - 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, - 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, - 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, - 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, - 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f }; - -/* * data that needs to be sent to device. sets up card internal stuff. * values dumped from windows driver and filtered by trial'n'error. */ @@ -59,7 +43,7 @@ init_data[] = { { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, - { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, + { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, { 0 } /* TERMINATING ENTRY */ @@ -70,20 +54,47 @@ static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; +static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0); +static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500); + enum { DIGITAL_THRU_ONLY_SAMPLERATE = 3 }; -static void usb6fire_control_master_vol_update(struct control_runtime *rt) +static void usb6fire_control_output_vol_update(struct control_runtime *rt) { struct comm_runtime *comm_rt = rt->chip->comm; - if (comm_rt) { - /* set volume */ - comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f - - log_volume_table[rt->master_vol]); - /* unmute */ - comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00); - } + int i; + + if (comm_rt) + for (i = 0; i < 6; i++) + if (!(rt->ovol_updated & (1 << i))) { + comm_rt->write8(comm_rt, 0x12, 0x0f + i, + 180 - rt->output_vol[i]); + rt->ovol_updated |= 1 << i; + } +} + +static void usb6fire_control_output_mute_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + + if (comm_rt) + comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute); +} + +static void usb6fire_control_input_vol_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + int i; + + if (comm_rt) + for (i = 0; i < 2; i++) + if (!(rt->ivol_updated & (1 << i))) { + comm_rt->write8(comm_rt, 0x12, 0x1c + i, + rt->input_vol[i] & 0x3f); + rt->ivol_updated |= 1 << i; + } } static void usb6fire_control_line_phono_update(struct control_runtime *rt) @@ -165,34 +176,147 @@ static int usb6fire_control_streaming_update(struct control_runtime *rt) return -EINVAL; } -static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, +static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; + uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 127; + uinfo->value.integer.max = 180; return 0; } -static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol, +static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; int changed = 0; - if (rt->master_vol != ucontrol->value.integer.value[0]) { - rt->master_vol = ucontrol->value.integer.value[0]; - usb6fire_control_master_vol_update(rt); + + if (ch > 4) { + snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); + return -EINVAL; + } + + if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { + rt->output_vol[ch] = ucontrol->value.integer.value[0]; + rt->ovol_updated &= ~(1 << ch); changed = 1; } + if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { + rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; + rt->ovol_updated &= ~(2 << ch); + changed = 1; + } + + if (changed) + usb6fire_control_output_vol_update(rt); + return changed; } -static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol, +static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct control_runtime *rt = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = rt->master_vol; + unsigned int ch = kcontrol->private_value; + + if (ch > 4) { + snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = rt->output_vol[ch]; + ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; + return 0; +} + +static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; + u8 old = rt->output_mute; + u8 value = 0; + + if (ch > 4) { + snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); + return -EINVAL; + } + + rt->output_mute &= ~(3 << ch); + if (ucontrol->value.integer.value[0]) + value |= 1; + if (ucontrol->value.integer.value[1]) + value |= 2; + rt->output_mute |= value << ch; + + if (rt->output_mute != old) + usb6fire_control_output_mute_update(rt); + + return rt->output_mute != old; +} + +static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; + u8 value = rt->output_mute >> ch; + + if (ch > 4) { + snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = 1 & value; + value >>= 1; + ucontrol->value.integer.value[1] = 1 & value; + + return 0; +} + +static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 30; + return 0; +} + +static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { + rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; + rt->ivol_updated &= ~(1 << 0); + changed = 1; + } + if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { + rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; + rt->ivol_updated &= ~(1 << 1); + changed = 1; + } + + if (changed) + usb6fire_control_input_vol_update(rt); + + return changed; +} + +static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = rt->input_vol[0] + 15; + ucontrol->value.integer.value[1] = rt->input_vol[1] + 15; + return 0; } @@ -287,18 +411,83 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, return 0; } -static struct __devinitdata snd_kcontrol_new elements[] = { +static struct __devinitdata snd_kcontrol_new vol_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", + .name = "Analog Playback Volume", .index = 0, + .private_value = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Volume", + .index = 1, + .private_value = 2, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Volume", + .index = 2, + .private_value = 4, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + {} +}; + +static struct __devinitdata snd_kcontrol_new mute_elements[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Switch", + .index = 0, + .private_value = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Switch", + .index = 1, + .private_value = 2, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = usb6fire_control_master_vol_info, - .get = usb6fire_control_master_vol_get, - .put = usb6fire_control_master_vol_put + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Switch", + .index = 2, + .private_value = 4, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, + }, + {} +}; + +static struct __devinitdata snd_kcontrol_new elements[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line/Phono Capture Route", .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -324,9 +513,54 @@ static struct __devinitdata snd_kcontrol_new elements[] = { .get = usb6fire_control_digital_thru_get, .put = usb6fire_control_digital_thru_put }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Capture Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_input_vol_info, + .get = usb6fire_control_input_vol_get, + .put = usb6fire_control_input_vol_put, + .tlv = { .p = tlv_input } + }, {} }; +static int usb6fire_control_add_virtual( + struct control_runtime *rt, + struct snd_card *card, + char *name, + struct snd_kcontrol_new *elems) +{ + int ret; + int i; + struct snd_kcontrol *vmaster = + snd_ctl_make_virtual_master(name, tlv_output); + struct snd_kcontrol *control; + + if (!vmaster) + return -ENOMEM; + ret = snd_ctl_add(card, vmaster); + if (ret < 0) + return ret; + + i = 0; + while (elems[i].name) { + control = snd_ctl_new1(&elems[i], rt); + if (!control) + return -ENOMEM; + ret = snd_ctl_add(card, control); + if (ret < 0) + return ret; + ret = snd_ctl_add_slave(vmaster, control); + if (ret < 0) + return ret; + i++; + } + return 0; +} + int __devinit usb6fire_control_init(struct sfire_chip *chip) { int i; @@ -352,9 +586,26 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) usb6fire_control_opt_coax_update(rt); usb6fire_control_line_phono_update(rt); - usb6fire_control_master_vol_update(rt); + usb6fire_control_output_vol_update(rt); + usb6fire_control_output_mute_update(rt); + usb6fire_control_input_vol_update(rt); usb6fire_control_streaming_update(rt); + ret = usb6fire_control_add_virtual(rt, chip->card, + "Master Playback Volume", vol_elements); + if (ret) { + snd_printk(KERN_ERR PREFIX "cannot add control.\n"); + kfree(rt); + return ret; + } + ret = usb6fire_control_add_virtual(rt, chip->card, + "Master Playback Switch", mute_elements); + if (ret) { + snd_printk(KERN_ERR PREFIX "cannot add control.\n"); + kfree(rt); + return ret; + } + i = 0; while (elements[i].name) { ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index 8f5aeead2e3d..9a596d95474a 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -44,7 +43,11 @@ struct control_runtime { bool line_phono_switch; bool digital_thru_switch; bool usb_streaming; - u8 master_vol; + u8 output_vol[6]; + u8 ovol_updated; + u8 output_mute; + s8 input_vol[2]; + u8 ivol_updated; }; int __devinit usb6fire_control_init(struct sfire_chip *chip); diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 3b5f517a3972..6f9715ab32fe 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 13f4509dce2b..f0e5179b242b 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h index 97a7bf669135..5114eccc1d8e 100644 --- a/sound/usb/6fire/midi.h +++ b/sound/usb/6fire/midi.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index d144cdb2f159..c97d05f0e966 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h index 2bee81374002..3104301b257d 100644 --- a/sound/usb/6fire/pcm.h +++ b/sound/usb/6fire/pcm.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 3efc21c3d67c..ff77b28f3da1 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -106,6 +106,7 @@ config SND_USB_6FIRE select BITREVERSE select SND_RAWMIDI select SND_PCM + select SND_VMASTER help Say Y here to include support for TerraTec 6fire DMX USB interface. diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0220b0f335b9..0eed6115c2d4 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -695,6 +695,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { struct audioformat *fp; + int *rate_list; int count = 0, needs_knot = 0; int err; @@ -708,7 +709,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, if (!needs_knot) return 0; - subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); + subs->rate_list.list = rate_list = + kmalloc(sizeof(int) * count, GFP_KERNEL); if (!subs->rate_list.list) return -ENOMEM; subs->rate_list.count = count; @@ -717,7 +719,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, list_for_each_entry(fp, &subs->fmt_list, list) { int i; for (i = 0; i < fp->nr_rates; i++) - subs->rate_list.list[count++] = fp->rate_table[i]; + rate_list[count++] = fp->rate_table[i]; } err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &subs->rate_list); diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 6ffb3713b60c..520ef96d7c75 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -80,7 +80,7 @@ static int usX2Y_urb_capt_retire(struct snd_usX2Y_substream *subs) cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ snd_printk(KERN_ERR "active frame status %i. " - "Most propably some hardware problem.\n", + "Most probably some hardware problem.\n", urb->iso_frame_desc[i].status); return urb->iso_frame_desc[i].status; } @@ -300,7 +300,7 @@ static void usX2Y_error_sequence(struct usX2Ydev *usX2Y, { snd_printk(KERN_ERR "Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n" -"Most propably some urb of usb-frame %i is still missing.\n" +"Most probably some urb of usb-frame %i is still missing.\n" "Cause could be too long delays in usb-hcd interrupt handling.\n", usb_get_current_frame_number(usX2Y->dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index a51340f6f2db..8e40b6e67e9e 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -74,7 +74,7 @@ static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs) } for (i = 0; i < nr_of_packs(); i++) { if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ - snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status); + snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status); return urb->iso_frame_desc[i].status; } lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride; |