diff options
Diffstat (limited to 'sound')
252 files changed, 31391 insertions, 25121 deletions
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 3ff8cc5f487a..010658335881 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -262,8 +262,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, */ dev->allocated_resource[i] = request_mem_region(dev->resources[i].start, - dev->resources[i].end - - dev->resources[i].start + 1, + resource_size(&dev->resources[i]), dev->rnames[i]); if (!dev->allocated_resource[i]) { printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); @@ -272,19 +271,19 @@ static int i2sbus_add_dev(struct macio_dev *macio, } r = &dev->resources[aoa_resource_i2smmio]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct i2s_interface_regs)) goto err; dev->intfregs = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_txdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->out.dbdma = ioremap(r->start, rlen); r = &dev->resources[aoa_resource_rxdbdma]; - rlen = r->end - r->start + 1; + rlen = resource_size(r); if (rlen < sizeof(struct dbdma_regs)) goto err; dev->in.dbdma = ioremap(r->start, rlen); diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index bfee60c4d4c0..6fd9391b3a6c 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -448,7 +448,7 @@ static int __devinit atmel_abdac_probe(struct platform_device *pdev) goto out_free_card; } - dac->regs = ioremap(regs->start, regs->end - regs->start + 1); + dac->regs = ioremap(regs->start, resource_size(regs)); if (!dac->regs) { dev_dbg(&pdev->dev, "could not remap register memory\n"); goto out_free_card; diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index ac35222ad0dd..6e5addeb236b 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -971,7 +971,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) chip->card = card; chip->pclk = pclk; chip->pdev = pdev; - chip->regs = ioremap(regs->start, regs->end - regs->start + 1); + chip->regs = ioremap(regs->start, resource_size(regs)); if (!chip->regs) { dev_dbg(&pdev->dev, "could not remap register memory\n"); diff --git a/sound/core/info.c b/sound/core/info.c index 7077f601da5a..601f0ebb677b 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -531,7 +531,7 @@ int __init snd_info_init(void) { struct proc_dir_entry *p; - p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + p = proc_mkdir("asound", NULL); if (p == NULL) return -ENOMEM; snd_proc_root = p; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f1341308beda..86d0caf91b35 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -128,7 +128,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } -static void pcm_debug_name(struct snd_pcm_substream *substream, +#ifdef CONFIG_SND_DEBUG +void snd_pcm_debug_name(struct snd_pcm_substream *substream, char *name, size_t len) { snprintf(name, len, "pcmC%dD%d%c:%d", @@ -137,6 +138,8 @@ static void pcm_debug_name(struct snd_pcm_substream *substream, substream->stream ? 'c' : 'p', substream->number); } +EXPORT_SYMBOL(snd_pcm_debug_name); +#endif #define XRUN_DEBUG_BASIC (1<<0) #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ @@ -168,7 +171,7 @@ static void xrun(struct snd_pcm_substream *substream) snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { char name[16]; - pcm_debug_name(substream, name, sizeof(name)); + snd_pcm_debug_name(substream, name, sizeof(name)); snd_printd(KERN_DEBUG "XRUN: %s\n", name); dump_stack_on_xrun(substream); } @@ -243,7 +246,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream) return; if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) return; - pcm_debug_name(substream, name, sizeof(name)); + snd_pcm_debug_name(substream, name, sizeof(name)); for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { entry = &log->entries[idx]; if (entry->period_size == 0) @@ -319,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (pos >= runtime->buffer_size) { if (printk_ratelimit()) { char name[16]; - pcm_debug_name(substream, name, sizeof(name)); + snd_pcm_debug_name(substream, name, sizeof(name)); xrun_log_show(substream); snd_printd(KERN_ERR "BUG: %s, pos = %ld, " "buffer size = %ld, period size = %ld\n", @@ -364,7 +367,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (xrun_debug(substream, in_interrupt ? XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { char name[16]; - pcm_debug_name(substream, name, sizeof(name)); + snd_pcm_debug_name(substream, name, sizeof(name)); snd_printd("%s_update: %s: pos=%u/%u/%u, " "hwptr=%ld/%ld/%ld/%ld\n", in_interrupt ? "period" : "hwptr", diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index cbbed0db9e56..849a0ed95054 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -92,16 +92,12 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre (!substream->append || runtime->avail >= count); } -static void snd_rawmidi_input_event_tasklet(unsigned long data) +static void snd_rawmidi_input_event_work(struct work_struct *work) { - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->runtime->event(substream); -} - -static void snd_rawmidi_output_trigger_tasklet(unsigned long data) -{ - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data; - substream->ops->trigger(substream, 1); + struct snd_rawmidi_runtime *runtime = + container_of(work, struct snd_rawmidi_runtime, event_work); + if (runtime->event) + runtime->event(runtime->substream); } static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) @@ -110,16 +106,10 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL) return -ENOMEM; + runtime->substream = substream; spin_lock_init(&runtime->lock); init_waitqueue_head(&runtime->sleep); - if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) - tasklet_init(&runtime->tasklet, - snd_rawmidi_input_event_tasklet, - (unsigned long)substream); - else - tasklet_init(&runtime->tasklet, - snd_rawmidi_output_trigger_tasklet, - (unsigned long)substream); + INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work); runtime->event = NULL; runtime->buffer_size = PAGE_SIZE; runtime->avail_min = 1; @@ -150,12 +140,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs { if (!substream->opened) return; - if (up) { - tasklet_schedule(&substream->runtime->tasklet); - } else { - tasklet_kill(&substream->runtime->tasklet); - substream->ops->trigger(substream, 0); - } + substream->ops->trigger(substream, up); } static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) @@ -163,8 +148,8 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i if (!substream->opened) return; substream->ops->trigger(substream, up); - if (!up && substream->runtime->event) - tasklet_kill(&substream->runtime->tasklet); + if (!up) + cancel_work_sync(&substream->runtime->event_work); } int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) @@ -641,10 +626,10 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = kmalloc(params->buffer_size, GFP_KERNEL); + newbuf = krealloc(runtime->buffer, params->buffer_size, + GFP_KERNEL); if (!newbuf) return -ENOMEM; - kfree(runtime->buffer); runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; runtime->avail = runtime->buffer_size; @@ -668,10 +653,10 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = kmalloc(params->buffer_size, GFP_KERNEL); + newbuf = krealloc(runtime->buffer, params->buffer_size, + GFP_KERNEL); if (!newbuf) return -ENOMEM; - kfree(runtime->buffer); runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; } @@ -926,7 +911,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, } if (result > 0) { if (runtime->event) - tasklet_schedule(&runtime->tasklet); + schedule_work(&runtime->event_work); else if (snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h index 4ff6c8cc5077..fc7a2dc410a1 100644 --- a/sound/drivers/pcsp/pcsp.h +++ b/sound/drivers/pcsp/pcsp.h @@ -10,14 +10,8 @@ #define __PCSP_H__ #include <linux/hrtimer.h> +#include <linux/i8253.h> #include <linux/timex.h> -#if defined(CONFIG_MIPS) || defined(CONFIG_X86) -/* Use the global PIT lock ! */ -#include <asm/i8253.h> -#else -#include <asm/8253pit.h> -static DEFINE_RAW_SPINLOCK(i8253_lock); -#endif #define PCSP_SOUND_VERSION 0x400 /* read 4.00 */ #define PCSP_DEBUG 0 diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 5466de8527bd..3fc257da180c 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -171,7 +171,7 @@ static int fwspk_open(struct snd_pcm_substream *substream) err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, 8192000); + 5000, UINT_MAX); if (err < 0) return err; diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h index 3773e242b58e..a168ba3313ac 100644 --- a/sound/isa/msnd/msnd.h +++ b/sound/isa/msnd/msnd.h @@ -249,7 +249,7 @@ struct snd_msnd { /* State variables */ enum { msndClassic, msndPinnacle } type; - mode_t mode; + fmode_t mode; unsigned long flags; #define F_RESETTING 0 #define F_HAVEDIGITAL 1 diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index 4d2a6ae978f7..8a197fd3c57e 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -458,7 +458,7 @@ static int ad1848_set_recmask(ad1848_info * devc, int mask) return mask; } -static void change_bits(ad1848_info * devc, unsigned char *regval, +static void oss_change_bits(ad1848_info *devc, unsigned char *regval, unsigned char *muteval, int dev, int chn, int newval) { unsigned char mask; @@ -516,10 +516,10 @@ static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int if (muteregoffs != regoffs) { muteval = ad_read(devc, muteregoffs); - change_bits(devc, &val, &muteval, dev, channel, value); + oss_change_bits(devc, &val, &muteval, dev, channel, value); } else - change_bits(devc, &val, &val, dev, channel, value); + oss_change_bits(devc, &val, &val, dev, channel, value); spin_lock_irqsave(&devc->lock,flags); ad_write(devc, regoffs, val); diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c index 2039d31b7e22..f8f3b7a66b73 100644 --- a/sound/oss/sb_mixer.c +++ b/sound/oss/sb_mixer.c @@ -232,7 +232,7 @@ static int detect_mixer(sb_devc * devc) return 1; } -static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) +static void oss_change_bits(sb_devc *devc, unsigned char *regval, int dev, int chn, int newval) { unsigned char mask; int shift; @@ -284,7 +284,7 @@ int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) return -EINVAL; val = sb_getmixer(devc, regoffs); - change_bits(devc, &val, dev, LEFT_CHN, left); + oss_change_bits(devc, &val, dev, LEFT_CHN, left); if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* * Change register @@ -304,7 +304,7 @@ int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) * Read the new one */ } - change_bits(devc, &val, dev, RIGHT_CHN, right); + oss_change_bits(devc, &val, dev, RIGHT_CHN, right); sb_setmixer(devc, regoffs, val); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index d8f6fd65ebbb..201503673f25 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -944,7 +944,7 @@ snd_ad1889_create(struct snd_card *card, spin_lock_init(&chip->lock); /* only now can we call ad1889_free */ if (request_irq(pci->irq, snd_ad1889_interrupt, - IRQF_SHARED, card->driver, chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq); snd_ad1889_free(chip); return -EBUSY; @@ -1055,7 +1055,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = { MODULE_DEVICE_TABLE(pci, snd_ad1889_ids); static struct pci_driver ad1889_pci_driver = { - .name = "AD1889 Audio", + .name = KBUILD_MODNAME, .id_table = snd_ad1889_ids, .probe = snd_ad1889_probe, .remove = __devexit_p(snd_ad1889_remove), diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 5c6e322a48f0..b444b74d9dcf 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -2090,7 +2090,7 @@ static int __devinit snd_ali_resources(struct snd_ali *codec) codec->port = pci_resource_start(codec->pci, 0); if (request_irq(codec->pci->irq, snd_ali_card_interrupt, - IRQF_SHARED, "ALI 5451", codec)) { + IRQF_SHARED, KBUILD_MODNAME, codec)) { snd_printk(KERN_ERR "Unable to request irq.\n"); return -EBUSY; } @@ -2295,7 +2295,7 @@ static void __devexit snd_ali_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ALI 5451", + .name = KBUILD_MODNAME, .id_table = snd_ali_ids, .probe = snd_ali_probe, .remove = __devexit_p(snd_ali_remove), diff --git a/sound/pci/als300.c b/sound/pci/als300.c index d7653cb7ac60..736c8e93db1f 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -722,7 +722,7 @@ static int __devinit snd_als300_create(struct snd_card *card, irq_handler = snd_als300_interrupt; if (request_irq(pci->irq, irq_handler, IRQF_SHARED, - card->shortname, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_als300_free(chip); return -EBUSY; @@ -846,7 +846,7 @@ static int __devinit snd_als300_probe(struct pci_dev *pci, } static struct pci_driver driver = { - .name = "ALS300", + .name = KBUILD_MODNAME, .id_table = snd_als300_ids, .probe = snd_als300_probe, .remove = __devexit_p(snd_als300_remove), diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 0e247cb90ecc..a9c1af33f276 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -1036,7 +1036,7 @@ static int snd_als4000_resume(struct pci_dev *pci) static struct pci_driver driver = { - .name = "ALS4000", + .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .remove = __devexit_p(snd_card_als4000_remove), diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index e3569bdd3b64..eae62ebbd295 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -41,29 +41,10 @@ #include <sound/tlv.h> #include <sound/hwdep.h> - MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); -#if defined CONFIG_SND_DEBUG -/* copied from pcm_lib.c, hope later patch will make that version public -and this copy can be removed */ -static void pcm_debug_name(struct snd_pcm_substream *substream, - char *name, size_t len) -{ - snprintf(name, len, "pcmC%dD%d%c:%d", - substream->pcm->card->number, - substream->pcm->device, - substream->stream ? 'c' : 'p', - substream->number); -} -#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name)) -#else -#define pcm_debug_name(s, n, l) do { } while (0) -#define DEBUG_NAME(name, substream) do { } while (0) -#endif - #if defined CONFIG_SND_DEBUG_VERBOSE /** * snd_printddd - very verbose debug printk @@ -304,7 +285,8 @@ static u16 handle_error(u16 err, int line, char *filename) static void print_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *p) { - DEBUG_NAME(substream, name); + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); snd_printd("%s HWPARAMS\n", name); snd_printd(" samplerate %d Hz\n", params_rate(p)); snd_printd(" channels %d\n", params_channels(p)); @@ -576,8 +558,9 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; u16 e; - DEBUG_NAME(substream, name); + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); snd_printdd("%s trigger\n", name); switch (cmd) { @@ -741,7 +724,9 @@ static void snd_card_asihpi_timer_function(unsigned long data) int loops = 0; u16 state; u32 buffer_size, bytes_avail, samples_played, on_card_bytes; - DEBUG_NAME(substream, name); + char name[16]; + + snd_pcm_debug_name(substream, name, sizeof(name)); snd_printdd("%s snd_card_asihpi_timer_function\n", name); @@ -1323,10 +1308,12 @@ static const char * const asihpi_src_names[] = { "RF", "Clock", "Bitstream", - "Microphone", - "Cobranet", + "Mic", + "Net", "Analog", "Adapter", + "RTP", + "GPI", }; compile_time_assert( @@ -1341,8 +1328,10 @@ static const char * const asihpi_dst_names[] = { "Digital", "RF", "Speaker", - "Cobranet Out", - "Analog" + "Net", + "Analog", + "RTP", + "GPO", }; compile_time_assert( @@ -1476,11 +1465,40 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); +#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info + +static int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u32 mute; + + hpi_handle_error(hpi_volume_get_mute(h_control, &mute)); + ucontrol->value.integer.value[0] = mute ? 0 : 1; + + return 0; +} + +static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + int change = 1; + /* HPI currently only supports all or none muting of multichannel volume + ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted + */ + int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS; + hpi_handle_error(hpi_volume_set_mute(h_control, mute)); + return change; +} + static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, struct hpi_control *hpi_ctl) { struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; + int err; + u32 mute; asihpi_ctl_init(&snd_control, hpi_ctl, "Volume"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -1490,7 +1508,19 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, snd_control.put = snd_asihpi_volume_put; snd_control.tlv.p = db_scale_100; - return ctl_add(card, &snd_control, asihpi); + err = ctl_add(card, &snd_control, asihpi); + if (err) + return err; + + if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) { + asihpi_ctl_init(&snd_control, hpi_ctl, "Switch"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_control.info = snd_asihpi_volume_mute_info; + snd_control.get = snd_asihpi_volume_mute_get; + snd_control.put = snd_asihpi_volume_mute_put; + err = ctl_add(card, &snd_control, asihpi); + } + return err; } /*------------------------------------------------------------ @@ -2923,7 +2953,7 @@ static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = { MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl); static struct pci_driver driver = { - .name = "asihpi", + .name = KBUILD_MODNAME, .id_table = asihpi_pci_tbl, .probe = snd_asihpi_probe, .remove = __devexit_p(snd_asihpi_remove), diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h index 255429c32c1c..f20727288994 100644 --- a/sound/pci/asihpi/hpi.h +++ b/sound/pci/asihpi/hpi.h @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -42,12 +42,11 @@ i.e 3.05.02 is a development version #define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF)) #define HPI_VER_RELEASE(v) ((int)(v & 0xFF)) -/* Use single digits for versions less that 10 to avoid octal. */ -#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 6, 0) -#define HPI_VER_STRING "4.06.00" +#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 8, 0) +#define HPI_VER_STRING "4.08.00" /* Library version as documented in hpi-api-versions.txt */ -#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0) +#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(10, 0, 0) #include <linux/types.h> #define HPI_BUILD_EXCLUDE_DEPRECATED @@ -211,8 +210,12 @@ enum HPI_SOURCENODES { HPI_SOURCENODE_COBRANET = 109, HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */ HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */ + /** RTP stream input node - This node is a destination for + packets of RTP audio samples from other devices. */ + HPI_SOURCENODE_RTP_DESTINATION = 112, + HPI_SOURCENODE_GP_IN = 113, /**< general purpose input. */ /* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */ - HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */ + HPI_SOURCENODE_LAST_INDEX = 113 /**< largest ID */ /* AX6 max sourcenode types = 15 */ }; @@ -228,7 +231,7 @@ enum HPI_DESTNODES { HPI_DESTNODE_NONE = 200, /** In Stream (Record) node. */ HPI_DESTNODE_ISTREAM = 201, - HPI_DESTNODE_LINEOUT = 202, /**< line out node. */ + HPI_DESTNODE_LINEOUT = 202, /**< line out node. */ HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */ HPI_DESTNODE_RF = 204, /**< RF output node. */ HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */ @@ -236,9 +239,12 @@ enum HPI_DESTNODES { Audio samples from the device are sent out on the Cobranet network.*/ HPI_DESTNODE_COBRANET = 206, HPI_DESTNODE_ANALOG = 207, /**< analog output node. */ - + /** RTP stream output node - This node is a source for + packets of RTP audio samples that are sent to other devices. */ + HPI_DESTNODE_RTP_SOURCE = 208, + HPI_DESTNODE_GP_OUT = 209, /**< general purpose output node. */ /* !!!Update this AND hpidebug.h if you add a new destnode type!!! */ - HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */ + HPI_DESTNODE_LAST_INDEX = 209 /**< largest ID */ /* AX6 max destnode types = 15 */ }; diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index df4aed5295dd..3cc6f11c20aa 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -359,7 +359,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr) HPI_ERROR_PROCESSING_MESSAGE); switch (phm->type) { - case HPI_TYPE_MESSAGE: + case HPI_TYPE_REQUEST: switch (phm->object) { case HPI_OBJ_SUBSYSTEM: subsys_message(phm, phr); @@ -538,7 +538,7 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n"); memset(&hm, 0, sizeof(hm)); - hm.type = HPI_TYPE_MESSAGE; + hm.type = HPI_TYPE_REQUEST; hm.size = sizeof(struct hpi_message); hm.object = HPI_OBJ_ADAPTER; hm.function = HPI_ADAPTER_GET_INFO; @@ -946,11 +946,8 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, } /* write the DSP code down into the DSPs memory */ - /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */ - dsp_code.ps_dev = pao->pci.pci_dev; - - error = hpi_dsp_code_open(boot_load_family, &dsp_code, - pos_error_code); + error = hpi_dsp_code_open(boot_load_family, pao->pci.pci_dev, + &dsp_code, pos_error_code); if (error) return error; diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 9d5df54a6b46..e041a6ae1c5a 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -373,6 +373,7 @@ static void instream_message(struct hpi_adapter_obj *pao, /** Entry point to this HPI backend * All calls to the HPI start here */ +static void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr) { @@ -392,7 +393,7 @@ void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm, HPI_DEBUG_LOG(VERBOSE, "start of switch\n"); switch (phm->type) { - case HPI_TYPE_MESSAGE: + case HPI_TYPE_REQUEST: switch (phm->object) { case HPI_OBJ_SUBSYSTEM: subsys_message(pao, phm, phr); @@ -402,7 +403,6 @@ void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm, adapter_message(pao, phm, phr); break; - case HPI_OBJ_CONTROLEX: case HPI_OBJ_CONTROL: control_message(pao, phm, phr); break; @@ -634,11 +634,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n"); memset(&hm, 0, sizeof(hm)); - hm.type = HPI_TYPE_MESSAGE; + /* wAdapterIndex == version == 0 */ + hm.type = HPI_TYPE_REQUEST; hm.size = sizeof(hm); hm.object = HPI_OBJ_ADAPTER; hm.function = HPI_ADAPTER_GET_INFO; - hm.adapter_index = 0; + memset(&hr, 0, sizeof(hr)); hr.size = sizeof(hr); @@ -658,9 +659,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, hr.u.ax.info.num_outstreams + hr.u.ax.info.num_instreams; - hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams, - 65536, pao->pci.pci_dev); - HPI_DEBUG_LOG(VERBOSE, "got adapter info type %x index %d serial %d\n", hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index, @@ -709,9 +707,6 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) [i]); phw->outstream_host_buffer_size[i] = 0; } - - hpios_locked_mem_unprepare(pao->pci.pci_dev); - kfree(phw); } @@ -1371,9 +1366,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, return err; /* write the DSP code down into the DSPs memory */ - dsp_code.ps_dev = pao->pci.pci_dev; - err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code, - pos_error_code); + err = hpi_dsp_code_open(boot_code_id[dsp], pao->pci.pci_dev, + &dsp_code, pos_error_code); if (err) return err; @@ -2084,13 +2078,13 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, u16 err = 0; message_count++; - if (phm->size > sizeof(interface->u)) { + if (phm->size > sizeof(interface->u.message_buffer)) { phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL; - phr->specific_error = sizeof(interface->u); + phr->specific_error = sizeof(interface->u.message_buffer); phr->size = sizeof(struct hpi_response_header); HPI_DEBUG_LOG(ERROR, "message len %d too big for buffer %zd \n", phm->size, - sizeof(interface->u)); + sizeof(interface->u.message_buffer)); return 0; } @@ -2122,18 +2116,19 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, /* read the result */ if (time_out) { - if (interface->u.response_buffer.size <= phr->size) + if (interface->u.response_buffer.response.size <= phr->size) memcpy(phr, &interface->u.response_buffer, - interface->u.response_buffer.size); + interface->u.response_buffer.response.size); else { HPI_DEBUG_LOG(ERROR, "response len %d too big for buffer %d\n", - interface->u.response_buffer.size, phr->size); + interface->u.response_buffer.response.size, + phr->size); memcpy(phr, &interface->u.response_buffer, sizeof(struct hpi_response_header)); phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; phr->specific_error = - interface->u.response_buffer.size; + interface->u.response_buffer.response.size; phr->size = sizeof(struct hpi_response_header); } } @@ -2202,23 +2197,6 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, phm->u.d.u.data.data_size, H620_HIF_GET_DATA); break; - case HPI_CONTROL_SET_STATE: - if (phm->object == HPI_OBJ_CONTROLEX - && phm->u.cx.attribute == HPI_COBRANET_SET_DATA) - err = hpi6205_transfer_data(pao, - phm->u.cx.u.cobranet_bigdata.pb_data, - phm->u.cx.u.cobranet_bigdata.byte_count, - H620_HIF_SEND_DATA); - break; - - case HPI_CONTROL_GET_STATE: - if (phm->object == HPI_OBJ_CONTROLEX - && phm->u.cx.attribute == HPI_COBRANET_GET_DATA) - err = hpi6205_transfer_data(pao, - phm->u.cx.u.cobranet_bigdata.pb_data, - phr->u.cx.u.cobranet_data.byte_count, - H620_HIF_GET_DATA); - break; } phr->error = err; diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h index df2f02c0c7b4..ec0827b633a6 100644 --- a/sound/pci/asihpi/hpi6205.h +++ b/sound/pci/asihpi/hpi6205.h @@ -1,7 +1,7 @@ /***************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -70,15 +70,28 @@ The Host located memory buffer that the 6205 will bus master in and out of. ************************************************************/ #define HPI6205_SIZEOF_DATA (16*1024) + +struct message_buffer_6205 { + struct hpi_message message; + char data[256]; +}; + +struct response_buffer_6205 { + struct hpi_response response; + char data[256]; +}; + +union buffer_6205 { + struct message_buffer_6205 message_buffer; + struct response_buffer_6205 response_buffer; + u8 b_data[HPI6205_SIZEOF_DATA]; +}; + struct bus_master_interface { u32 host_cmd; u32 dsp_ack; u32 transfer_size_in_bytes; - union { - struct hpi_message_header message_buffer; - struct hpi_response_header response_buffer; - u8 b_data[HPI6205_SIZEOF_DATA]; - } u; + union buffer_6205 u; struct controlcache_6205 control_cache; struct async_event_buffer_6205 async_buffer; struct hpi_hostbuffer_status diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index bf5eced76bac..d497030c160f 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -32,12 +32,6 @@ HPI internal definitions #include "hpios.h" /* physical memory allocation */ -void hpios_locked_mem_init(void - ); -void hpios_locked_mem_free_all(void - ); -#define hpios_locked_mem_prepare(a, b, c, d); -#define hpios_locked_mem_unprepare(a) /** Allocate and map an area of locked memory for bus master DMA operations. @@ -226,8 +220,8 @@ enum HPI_CONTROL_ATTRIBUTES { HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1), HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2), - HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3), - HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4), + /*HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3), */ + /*HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4), */ HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5), HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6), HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7), @@ -364,10 +358,12 @@ Used in DLL to indicate device not present #define HPI_ADAPTER_ASI(f) (f) enum HPI_MESSAGE_TYPES { - HPI_TYPE_MESSAGE = 1, + HPI_TYPE_REQUEST = 1, HPI_TYPE_RESPONSE = 2, HPI_TYPE_DATA = 3, - HPI_TYPE_SSX2BYPASS_MESSAGE = 4 + HPI_TYPE_SSX2BYPASS_MESSAGE = 4, + HPI_TYPE_COMMAND = 5, + HPI_TYPE_NOTIFICATION = 6 }; enum HPI_OBJECT_TYPES { @@ -383,7 +379,7 @@ enum HPI_OBJECT_TYPES { HPI_OBJ_WATCHDOG = 10, HPI_OBJ_CLOCK = 11, HPI_OBJ_PROFILE = 12, - HPI_OBJ_CONTROLEX = 13, + /* HPI_ OBJ_ CONTROLEX = 13, */ HPI_OBJ_ASYNCEVENT = 14 #define HPI_OBJ_MAXINDEX 14 }; @@ -608,7 +604,7 @@ struct hpi_data_compat32 { #endif struct hpi_buffer { - /** placehoder for backward compatibility (see dwBufferSize) */ + /** placeholder for backward compatibility (see dwBufferSize) */ struct hpi_msg_format reserved; u32 command; /**< HPI_BUFFER_CMD_xxx*/ u32 pci_address; /**< PCI physical address of buffer for DSP DMA */ @@ -912,95 +908,13 @@ union hpi_control_union_res { u32 remaining_chars; } chars8; char c_data12[12]; -}; - -/* HPI_CONTROLX_STRUCTURES */ - -/* Message */ - -/** Used for all HMI variables where max length <= 8 bytes -*/ -struct hpi_controlx_msg_cobranet_data { - u32 hmi_address; - u32 byte_count; - u32 data[2]; -}; - -/** Used for string data, and for packet bridge -*/ -struct hpi_controlx_msg_cobranet_bigdata { - u32 hmi_address; - u32 byte_count; - u8 *pb_data; -#ifndef HPI64BIT - u32 padding; -#endif -}; - -/** Used for PADS control reading of string fields. -*/ -struct hpi_controlx_msg_pad_data { - u32 field; - u32 byte_count; - u8 *pb_data; -#ifndef HPI64BIT - u32 padding; -#endif -}; - -/** Used for generic data -*/ - -struct hpi_controlx_msg_generic { - u32 param1; - u32 param2; -}; - -struct hpi_controlx_msg { - u16 attribute; /* control attribute or property */ - u16 saved_index; - union { - struct hpi_controlx_msg_cobranet_data cobranet_data; - struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata; - struct hpi_controlx_msg_generic generic; - struct hpi_controlx_msg_pad_data pad_data; - /*struct param_value universal_value; */ - /* nothing extra to send for status read */ - } u; -}; - -/* Response */ -/** -*/ -struct hpi_controlx_res_cobranet_data { - u32 byte_count; - u32 data[2]; -}; - -struct hpi_controlx_res_cobranet_bigdata { - u32 byte_count; -}; - -struct hpi_controlx_res_cobranet_status { - u32 status; - u32 readable_size; - u32 writeable_size; -}; - -struct hpi_controlx_res_generic { - u32 param1; - u32 param2; -}; - -struct hpi_controlx_res { union { - struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata; - struct hpi_controlx_res_cobranet_data cobranet_data; - struct hpi_controlx_res_cobranet_status cobranet_status; - struct hpi_controlx_res_generic generic; - /*struct param_info universal_info; */ - /*struct param_value universal_value; */ - } u; + struct { + u32 status; + u32 readable_size; + u32 writeable_size; + } status; + } cobranet; }; struct hpi_nvmemory_msg { @@ -1126,7 +1040,6 @@ struct hpi_message { /* identical to struct hpi_control_msg, but field naming is improved */ struct hpi_control_union_msg cu; - struct hpi_controlx_msg cx; /* extended mixer control; */ struct hpi_nvmemory_msg n; struct hpi_gpio_msg l; /* digital i/o */ struct hpi_watchdog_msg w; @@ -1151,7 +1064,7 @@ struct hpi_message { sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\ sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\ sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\ - sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\ + sizeof(struct hpi_message_header), /* controlx obj removed */ \ sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \ } @@ -1188,7 +1101,6 @@ struct hpi_response { struct hpi_control_res c; /* mixer control; */ /* identical to hpi_control_res, but field naming is improved */ union hpi_control_union_res cu; - struct hpi_controlx_res cx; /* extended mixer control; */ struct hpi_nvmemory_res n; struct hpi_gpio_res l; /* digital i/o */ struct hpi_watchdog_res w; @@ -1213,7 +1125,7 @@ struct hpi_response { sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\ sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\ sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\ - sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\ + sizeof(struct hpi_response_header), /* controlx obj removed */ \ sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \ } @@ -1308,6 +1220,30 @@ struct hpi_res_adapter_debug_read { u8 bytes[256]; }; +struct hpi_msg_cobranet_hmi { + u16 attribute; + u16 padding; + u32 hmi_address; + u32 byte_count; +}; + +struct hpi_msg_cobranet_hmiwrite { + struct hpi_message_header h; + struct hpi_msg_cobranet_hmi p; + u8 bytes[256]; +}; + +struct hpi_msg_cobranet_hmiread { + struct hpi_message_header h; + struct hpi_msg_cobranet_hmi p; +}; + +struct hpi_res_cobranet_hmiread { + struct hpi_response_header h; + u32 byte_count; + u8 bytes[256]; +}; + #if 1 #define hpi_message_header_v1 hpi_message_header #define hpi_response_header_v1 hpi_response_header @@ -1338,7 +1274,6 @@ struct hpi_msg_payload_v0 { union hpi_mixerx_msg mx; struct hpi_control_msg c; struct hpi_control_union_msg cu; - struct hpi_controlx_msg cx; struct hpi_nvmemory_msg n; struct hpi_gpio_msg l; struct hpi_watchdog_msg w; @@ -1358,7 +1293,6 @@ struct hpi_res_payload_v0 { union hpi_mixerx_res mx; struct hpi_control_res c; union hpi_control_union_res cu; - struct hpi_controlx_res cx; struct hpi_nvmemory_res n; struct hpi_gpio_res l; struct hpi_watchdog_res w; @@ -1493,12 +1427,6 @@ struct hpi_control_cache_microphone { char temp_padding[6]; }; -struct hpi_control_cache_generic { - struct hpi_control_cache_info i; - u32 dw1; - u32 dw2; -}; - struct hpi_control_cache_single { union { struct hpi_control_cache_info i; @@ -1514,7 +1442,6 @@ struct hpi_control_cache_single { struct hpi_control_cache_silencedetector silence; struct hpi_control_cache_sampleclock clk; struct hpi_control_cache_microphone microphone; - struct hpi_control_cache_generic generic; } u; }; diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index b15a02e91f82..65b7ca13115b 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -57,7 +57,7 @@ u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr) } if (phr->function != phm->function) { - HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", + HPI_DEBUG_LOG(ERROR, "header function %d invalid\n", phr->function); return HPI_ERROR_INVALID_RESPONSE; } @@ -315,8 +315,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, short found = 1; struct hpi_control_cache_info *pI; struct hpi_control_cache_single *pC; - struct hpi_control_cache_pad *p_pad; - + size_t response_size; if (!find_control(phm->obj_index, p_cache, &pI)) { HPI_DEBUG_LOG(VERBOSE, "HPICMN find_control() failed for adap %d\n", @@ -326,11 +325,15 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, phr->error = 0; + /* set the default response size */ + response_size = + sizeof(struct hpi_response_header) + + sizeof(struct hpi_control_res); + /* pC is the default cached control strucure. May be cast to something else in the following switch statement. */ pC = (struct hpi_control_cache_single *)pI; - p_pad = (struct hpi_control_cache_pad *)pI; switch (pI->control_type) { @@ -529,9 +532,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, pI->control_index, pI->control_type, phm->u.c.attribute); if (found) - phr->size = - sizeof(struct hpi_response_header) + - sizeof(struct hpi_control_res); + phr->size = (u16)response_size; return found; } @@ -682,7 +683,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr) { switch (phm->type) { - case HPI_TYPE_MESSAGE: + case HPI_TYPE_REQUEST: switch (phm->object) { case HPI_OBJ_SUBSYSTEM: subsys_message(phm, phr); diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c index 5c6ea113d219..3a7afa31c1d8 100644 --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -1,8 +1,8 @@ /***********************************************************************/ -/*! +/** AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -18,90 +18,59 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \file -Functions for reading DSP code to load into DSP - -(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using +Functions for reading DSP code using hotplug firmware loader from individual dsp code files - -If neither of the above is defined, code is read from linked arrays. -DSPCODE_ARRAY is defined. - -HPI_INCLUDE_**** must be defined -and the appropriate hzz?????.c or hex?????.c linked in - - */ +*/ /***********************************************************************/ #define SOURCEFILE_NAME "hpidspcd.c" #include "hpidspcd.h" #include "hpidebug.h" -/** - Header structure for binary dsp code file (see asidsp.doc) - This structure must match that used in s2bin.c for generation of asidsp.bin - */ - -#ifndef DISABLE_PRAGMA_PACK1 -#pragma pack(push, 1) -#endif - -struct code_header { - u32 size; - char type[4]; - u32 adapter; - u32 version; - u32 crc; +struct dsp_code_private { + /** Firmware descriptor */ + const struct firmware *firmware; + struct pci_dev *dev; }; -#ifndef DISABLE_PRAGMA_PACK1 -#pragma pack(pop) -#endif - #define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \ HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER))) -/***********************************************************************/ -#include <linux/pci.h> /*-------------------------------------------------------------------*/ -short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code, - u32 *pos_error_code) +short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, + u32 *os_error_code) { - const struct firmware *ps_firmware = ps_dsp_code->ps_firmware; + const struct firmware *firmware; + struct pci_dev *dev = os_data; struct code_header header; char fw_name[20]; int err; sprintf(fw_name, "asihpi/dsp%04x.bin", adapter); - err = request_firmware(&ps_firmware, fw_name, - &ps_dsp_code->ps_dev->dev); + err = request_firmware(&firmware, fw_name, &dev->dev); - if (err != 0) { - dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + if (err || !firmware) { + dev_printk(KERN_ERR, &dev->dev, "%d, request_firmware failed for %s\n", err, fw_name); goto error1; } - if (ps_firmware->size < sizeof(header)) { - dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, - "Header size too small %s\n", fw_name); - goto error2; - } - memcpy(&header, ps_firmware->data, sizeof(header)); - if (header.adapter != adapter) { - dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, - "Adapter type incorrect %4x != %4x\n", header.adapter, - adapter); + if (firmware->size < sizeof(header)) { + dev_printk(KERN_ERR, &dev->dev, "Header size too small %s\n", + fw_name); goto error2; } - if (header.size != ps_firmware->size) { - dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, - "Code size wrong %d != %ld\n", header.size, - (unsigned long)ps_firmware->size); + memcpy(&header, firmware->data, sizeof(header)); + + if ((header.type != 0x45444F43) || /* "CODE" */ + (header.adapter != adapter) + || (header.size != firmware->size)) { + dev_printk(KERN_ERR, &dev->dev, "Invalid firmware file\n"); goto error2; } - if (header.version / 100 != HPI_VER_DECIMAL / 100) { - dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + if ((header.version / 100 & ~1) != (HPI_VER_DECIMAL / 100 & ~1)) { + dev_printk(KERN_ERR, &dev->dev, "Incompatible firmware version " "DSP image %d != Driver %d\n", header.version, HPI_VER_DECIMAL); @@ -109,67 +78,70 @@ short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code, } if (header.version != HPI_VER_DECIMAL) { - dev_printk(KERN_WARNING, &ps_dsp_code->ps_dev->dev, + dev_printk(KERN_WARNING, &dev->dev, "Firmware: release version mismatch DSP image %d != Driver %d\n", header.version, HPI_VER_DECIMAL); } HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name); - ps_dsp_code->ps_firmware = ps_firmware; - ps_dsp_code->block_length = header.size / sizeof(u32); - ps_dsp_code->word_count = sizeof(header) / sizeof(u32); - ps_dsp_code->version = header.version; - ps_dsp_code->crc = header.crc; + dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL); + if (!dsp_code->pvt) + return HPI_ERROR_MEMORY_ALLOC; + + dsp_code->pvt->dev = dev; + dsp_code->pvt->firmware = firmware; + dsp_code->header = header; + dsp_code->block_length = header.size / sizeof(u32); + dsp_code->word_count = sizeof(header) / sizeof(u32); return 0; error2: - release_firmware(ps_firmware); + release_firmware(firmware); error1: - ps_dsp_code->ps_firmware = NULL; - ps_dsp_code->block_length = 0; + dsp_code->block_length = 0; return HPI_ERROR_DSP_FILE_NOT_FOUND; } /*-------------------------------------------------------------------*/ -void hpi_dsp_code_close(struct dsp_code *ps_dsp_code) +void hpi_dsp_code_close(struct dsp_code *dsp_code) { - if (ps_dsp_code->ps_firmware != NULL) { + if (dsp_code->pvt->firmware) { HPI_DEBUG_LOG(DEBUG, "dsp code closed\n"); - release_firmware(ps_dsp_code->ps_firmware); - ps_dsp_code->ps_firmware = NULL; + release_firmware(dsp_code->pvt->firmware); + dsp_code->pvt->firmware = NULL; } + kfree(dsp_code->pvt); } /*-------------------------------------------------------------------*/ -void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code) +void hpi_dsp_code_rewind(struct dsp_code *dsp_code) { /* Go back to start of data, after header */ - ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32); + dsp_code->word_count = sizeof(struct code_header) / sizeof(u32); } /*-------------------------------------------------------------------*/ -short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword) +short hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword) { - if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length) + if (dsp_code->word_count + 1 > dsp_code->block_length) return HPI_ERROR_DSP_FILE_FORMAT; - *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code-> + *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code-> word_count]; - ps_dsp_code->word_count++; + dsp_code->word_count++; return 0; } /*-------------------------------------------------------------------*/ short hpi_dsp_code_read_block(size_t words_requested, - struct dsp_code *ps_dsp_code, u32 **ppblock) + struct dsp_code *dsp_code, u32 **ppblock) { - if (ps_dsp_code->word_count + words_requested > - ps_dsp_code->block_length) + if (dsp_code->word_count + words_requested > dsp_code->block_length) return HPI_ERROR_DSP_FILE_FORMAT; *ppblock = - ((u32 *)(ps_dsp_code->ps_firmware->data)) + - ps_dsp_code->word_count; - ps_dsp_code->word_count += words_requested; + ((u32 *)(dsp_code->pvt->firmware->data)) + + dsp_code->word_count; + dsp_code->word_count += words_requested; return 0; } diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h index 65f0ca732704..b22881122f19 100644 --- a/sound/pci/asihpi/hpidspcd.h +++ b/sound/pci/asihpi/hpidspcd.h @@ -2,7 +2,7 @@ /** AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -20,19 +20,6 @@ \file Functions for reading DSP code to load into DSP - hpi_dspcode_defines HPI DSP code loading method -Define exactly one of these to select how the DSP code is supplied to -the adapter. - -End users writing applications that use the HPI interface do not have to -use any of the below defines; they are only necessary for building drivers - -HPI_DSPCODE_FILE: -DSP code is supplied as a file that is opened and read from by the driver. - -HPI_DSPCODE_FIRMWARE: -DSP code is read using the hotplug firmware loader module. - Only valid when compiling the HPI kernel driver under Linux. */ /***********************************************************************/ #ifndef _HPIDSPCD_H_ @@ -40,37 +27,56 @@ DSP code is read using the hotplug firmware loader module. #include "hpi_internal.h" -#ifndef DISABLE_PRAGMA_PACK1 -#pragma pack(push, 1) -#endif +/** Code header version is decimal encoded e.g. 4.06.10 is 40601 */ +#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \ +HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER))) + +/** Header structure for dsp firmware file + This structure must match that used in s2bin.c for generation of asidsp.bin + */ +/*#ifndef DISABLE_PRAGMA_PACK1 */ +/*#pragma pack(push, 1) */ +/*#endif */ +struct code_header { + /** Size in bytes including header */ + u32 size; + /** File type tag "CODE" == 0x45444F43 */ + u32 type; + /** Adapter model number */ + u32 adapter; + /** Firmware version*/ + u32 version; + /** Data checksum */ + u32 checksum; +}; +/*#ifndef DISABLE_PRAGMA_PACK1 */ +/*#pragma pack(pop) */ +/*#endif */ + +/*? Don't need the pragmas? */ +compile_time_assert((sizeof(struct code_header) == 20), code_header_size); /** Descriptor for dspcode from firmware loader */ struct dsp_code { - /** Firmware descriptor */ - const struct firmware *ps_firmware; - struct pci_dev *ps_dev; + /** copy of file header */ + struct code_header header; /** Expected number of words in the whole dsp code,INCL header */ - long int block_length; + u32 block_length; /** Number of words read so far */ - long int word_count; - /** Version read from dsp code file */ - u32 version; - /** CRC read from dsp code file */ - u32 crc; -}; + u32 word_count; -#ifndef DISABLE_PRAGMA_PACK1 -#pragma pack(pop) -#endif + /** internal state of DSP code reader */ + struct dsp_code_private *pvt; +}; -/** Prepare *psDspCode to refer to the requuested adapter. - Searches the file, or selects the appropriate linked array +/** Prepare *psDspCode to refer to the requested adapter's firmware. +Code file name is obtained from HpiOs_GetDspCodePath \return 0 for success, or error code if requested code is not available */ short hpi_dsp_code_open( /** Code identifier, usually adapter family */ - u32 adapter, + u32 adapter, void *pci_dev, /** Pointer to DSP code control structure */ struct dsp_code *ps_dsp_code, /** Pointer to dword to receive OS specific error code */ diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index 7397b169b89f..ebb568d695f1 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -1663,68 +1663,64 @@ u16 hpi_channel_mode_get(u32 h_control, u16 *mode) u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count, u8 *pb_data) { - struct hpi_message hm; - struct hpi_response hr; + struct hpi_msg_cobranet_hmiwrite hm; + struct hpi_response_header hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, - HPI_CONTROL_SET_STATE); - if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) - return HPI_ERROR_INVALID_HANDLE; + hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr, sizeof(hr), + HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - hm.u.cx.u.cobranet_data.byte_count = byte_count; - hm.u.cx.u.cobranet_data.hmi_address = hmi_address; + if (hpi_handle_indexes(h_control, &hm.h.adapter_index, + &hm.h.obj_index)) + return HPI_ERROR_INVALID_HANDLE; - if (byte_count <= 8) { - memcpy(hm.u.cx.u.cobranet_data.data, pb_data, byte_count); - hm.u.cx.attribute = HPI_COBRANET_SET; - } else { - hm.u.cx.u.cobranet_bigdata.pb_data = pb_data; - hm.u.cx.attribute = HPI_COBRANET_SET_DATA; - } + if (byte_count > sizeof(hm.bytes)) + return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL; - hpi_send_recv(&hm, &hr); + hm.p.attribute = HPI_COBRANET_SET; + hm.p.byte_count = byte_count; + hm.p.hmi_address = hmi_address; + memcpy(hm.bytes, pb_data, byte_count); + hm.h.size = (u16)(sizeof(hm.h) + sizeof(hm.p) + byte_count); + hpi_send_recvV1(&hm.h, &hr); return hr.error; } u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data) { - struct hpi_message hm; - struct hpi_response hr; + struct hpi_msg_cobranet_hmiread hm; + struct hpi_res_cobranet_hmiread hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, - HPI_CONTROL_GET_STATE); - if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr), + HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); + + if (hpi_handle_indexes(h_control, &hm.h.adapter_index, + &hm.h.obj_index)) return HPI_ERROR_INVALID_HANDLE; - hm.u.cx.u.cobranet_data.byte_count = max_byte_count; - hm.u.cx.u.cobranet_data.hmi_address = hmi_address; + if (max_byte_count > sizeof(hr.bytes)) + return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; - if (max_byte_count <= 8) { - hm.u.cx.attribute = HPI_COBRANET_GET; - } else { - hm.u.cx.u.cobranet_bigdata.pb_data = pb_data; - hm.u.cx.attribute = HPI_COBRANET_GET_DATA; - } + hm.p.attribute = HPI_COBRANET_GET; + hm.p.byte_count = max_byte_count; + hm.p.hmi_address = hmi_address; - hpi_send_recv(&hm, &hr); - if (!hr.error && pb_data) { + hpi_send_recvV1(&hm.h, &hr.h); - *pbyte_count = hr.u.cx.u.cobranet_data.byte_count; + if (!hr.h.error && pb_data) { + if (hr.byte_count > sizeof(hr.bytes)) - if (*pbyte_count < max_byte_count) - max_byte_count = *pbyte_count; + return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; - if (hm.u.cx.attribute == HPI_COBRANET_GET) { - memcpy(pb_data, hr.u.cx.u.cobranet_data.data, - max_byte_count); - } else { + *pbyte_count = hr.byte_count; - } + if (hr.byte_count < max_byte_count) + max_byte_count = *pbyte_count; + memcpy(pb_data, hr.bytes, max_byte_count); } - return hr.error; + return hr.h.error; } u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus, @@ -1733,23 +1729,23 @@ u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus, struct hpi_message hm; struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) return HPI_ERROR_INVALID_HANDLE; - hm.u.cx.attribute = HPI_COBRANET_GET_STATUS; + hm.u.c.attribute = HPI_COBRANET_GET_STATUS; hpi_send_recv(&hm, &hr); if (!hr.error) { if (pstatus) - *pstatus = hr.u.cx.u.cobranet_status.status; + *pstatus = hr.u.cu.cobranet.status.status; if (preadable_size) *preadable_size = - hr.u.cx.u.cobranet_status.readable_size; + hr.u.cu.cobranet.status.readable_size; if (pwriteable_size) *pwriteable_size = - hr.u.cx.u.cobranet_status.writeable_size; + hr.u.cu.cobranet.status.writeable_size; } return hr.error; } diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c index 628376ce4a49..52400a6b5f15 100644 --- a/sound/pci/asihpi/hpimsginit.c +++ b/sound/pci/asihpi/hpimsginit.c @@ -46,7 +46,7 @@ static void hpi_init_message(struct hpi_message *phm, u16 object, if (gwSSX2_bypass) phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE; else - phm->type = HPI_TYPE_MESSAGE; + phm->type = HPI_TYPE_REQUEST; phm->object = object; phm->function = function; phm->version = 0; @@ -89,7 +89,7 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, memset(phm, 0, sizeof(*phm)); if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) { phm->size = size; - phm->type = HPI_TYPE_MESSAGE; + phm->type = HPI_TYPE_REQUEST; phm->object = object; phm->function = function; phm->version = 1; diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index 7352a5f7b4f7..2e779421a618 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -Extended Message Function With Response Cacheing +Extended Message Function With Response Caching (C) Copyright AudioScience Inc. 2002 *****************************************************************************/ @@ -186,7 +186,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, /* Initialize this module's internal state */ hpios_msgxlock_init(&msgx_lock); memset(&hpi_entry_points, 0, sizeof(hpi_entry_points)); - hpios_locked_mem_init(); /* Init subsys_findadapters response to no-adapters */ HPIMSGX__reset(HPIMSGX_ALLADAPTERS); hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, @@ -197,7 +196,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, case HPI_SUBSYS_DRIVER_UNLOAD: HPI_COMMON(phm, phr); HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner); - hpios_locked_mem_free_all(); hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_DRIVER_UNLOAD, 0); return; @@ -315,7 +313,7 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, { HPI_DEBUG_MESSAGE(DEBUG, phm); - if (phm->type != HPI_TYPE_MESSAGE) { + if (phm->type != HPI_TYPE_REQUEST) { hpi_init_response(phr, phm->object, phm->function, HPI_ERROR_INVALID_TYPE); return; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index d8e7047512f8..9683f84ecdc8 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -1,7 +1,7 @@ /******************************************************************************* AudioScience HPI driver - Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -107,7 +107,6 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) union hpi_response_buffer_v1 *hr; u16 res_max_size; u32 uncopied_bytes; - struct hpi_adapter *pa = NULL; int err = 0; if (cmd != HPI_IOCTL_LINUX) @@ -157,11 +156,6 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } - if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) { - err = -EINVAL; - goto out; - } - switch (hm->h.function) { case HPI_SUBSYS_CREATE_ADAPTER: case HPI_ADAPTER_DELETE: @@ -187,9 +181,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* -1=no data 0=read from user mem, 1=write to user mem */ int wrflag = -1; u32 adapter = hm->h.adapter_index; - pa = &adapters[adapter]; + struct hpi_adapter *pa = &adapters[adapter]; - if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) { + if ((adapter >= HPI_MAX_ADAPTERS) || (!pa->type)) { hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER, HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER_NUMBER); @@ -203,7 +197,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } - if (mutex_lock_interruptible(&adapters[adapter].mutex)) { + if (mutex_lock_interruptible(&pa->mutex)) { err = -EINTR; goto out; } @@ -239,8 +233,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) "stream buffer size %d\n", size); - mutex_unlock(&adapters - [adapter].mutex); + mutex_unlock(&pa->mutex); err = -EINVAL; goto out; } @@ -281,7 +274,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) uncopied_bytes, size); } - mutex_unlock(&adapters[adapter].mutex); + mutex_unlock(&pa->mutex); } /* on return response size must be set */ diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c index 742ee12a9e17..ff2a19b544fa 100644 --- a/sound/pci/asihpi/hpios.c +++ b/sound/pci/asihpi/hpios.c @@ -39,10 +39,6 @@ void hpios_delay_micro_seconds(u32 num_micro_sec) } -void hpios_locked_mem_init(void) -{ -} - /** Allocated an area of locked memory for bus master DMA operations. On error, return -ENOMEM, and *pMemArea.size = 0 @@ -85,7 +81,3 @@ u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area) return 1; } } - -void hpios_locked_mem_free_all(void) -{ -} diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index 03273e729f99..2f605e34bad0 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -38,6 +38,7 @@ HPI Operating System Specific macros for Linux Kernel driver #include <linux/firmware.h> #include <linux/interrupt.h> #include <linux/pci.h> +#include <linux/mutex.h> #define HPI_NO_OS_FILE_OPS diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 3119cd97a217..537e0a2cc68a 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1624,7 +1624,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card, } if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED, - card->shortname, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_atiixp_free(chip); return -EBUSY; @@ -1701,7 +1701,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ATI IXP AC97 controller", + .name = KBUILD_MODNAME, .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 2f74c2fdf1ea..45df275c8248 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1260,7 +1260,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card, } if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED, - card->shortname, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_atiixp_free(chip); return -EBUSY; @@ -1332,7 +1332,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ATI IXP MC97 controller", + .name = KBUILD_MODNAME, .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 7b72c88e449d..a38469986885 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -196,7 +196,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) } if ((err = request_irq(pci->irq, vortex_interrupt, - IRQF_SHARED, CARD_NAME_SHORT, + IRQF_SHARED, KBUILD_MODNAME, chip)) != 0) { printk(KERN_ERR "cannot grab irq\n"); goto irq_out; @@ -375,7 +375,7 @@ static void __devexit snd_vortex_remove(struct pci_dev *pci) // pci_driver definition static struct pci_driver driver = { - .name = CARD_NAME_SHORT, + .name = KBUILD_MODNAME, .id_table = snd_vortex_ids, .probe = snd_vortex_probe, .remove = __devexit_p(snd_vortex_remove), diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index c15002242d98..f8569b11331b 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -171,7 +171,7 @@ MODULE_DEVICE_TABLE(pci, snd_aw2_ids); /* pci_driver definition */ static struct pci_driver driver = { - .name = "Emagic Audiowerk 2", + .name = KBUILD_MODNAME, .id_table = snd_aw2_ids, .probe = snd_aw2_probe, .remove = __devexit_p(snd_aw2_remove), @@ -317,7 +317,7 @@ static int __devinit snd_aw2_create(struct snd_card *card, snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt); if (request_irq(pci->irq, snd_aw2_saa7146_interrupt, - IRQF_SHARED, "Audiowerk2", chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq); iounmap(chip->iobase_virt); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 9b7a6346037a..e4d76a270c9f 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2559,7 +2559,7 @@ snd_azf3328_create(struct snd_card *card, codec_setup->name = "I2S_OUT"; if (request_irq(pci->irq, snd_azf3328_interrupt, - IRQF_SHARED, card->shortname, chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto out_err; @@ -2860,7 +2860,7 @@ snd_azf3328_resume(struct pci_dev *pci) static struct pci_driver driver = { - .name = "AZF3328", + .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .remove = __devexit_p(snd_azf3328_remove), diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 2958a05b5293..39180335c237 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -760,7 +760,7 @@ static int __devinit snd_bt87x_create(struct snd_card *card, snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS); err = request_irq(pci->irq, snd_bt87x_interrupt, IRQF_SHARED, - "Bt87x audio", chip); + KBUILD_MODNAME, chip); if (err < 0) { snd_printk(KERN_ERR "cannot grab irq %d\n", pci->irq); goto fail; @@ -965,7 +965,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = { }; static struct pci_driver driver = { - .name = "Bt87x", + .name = KBUILD_MODNAME, .id_table = snd_bt87x_ids, .probe = snd_bt87x_probe, .remove = __devexit_p(snd_bt87x_remove), diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 437759239694..061b7e654586 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1666,7 +1666,7 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card, } if (request_irq(pci->irq, snd_ca0106_interrupt, - IRQF_SHARED, "snd_ca0106", chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { snd_ca0106_free(chip); printk(KERN_ERR "cannot grab irq\n"); return -EBUSY; @@ -1933,7 +1933,7 @@ MODULE_DEVICE_TABLE(pci, snd_ca0106_ids); // pci_driver definition static struct pci_driver driver = { - .name = "CA0106", + .name = KBUILD_MODNAME, .id_table = snd_ca0106_ids, .probe = snd_ca0106_probe, .remove = __devexit_p(snd_ca0106_remove), diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index f4e573555da3..9cf99fb7eb9c 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3053,7 +3053,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc cm->iobase = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_cmipci_interrupt, - IRQF_SHARED, card->driver, cm)) { + IRQF_SHARED, KBUILD_MODNAME, cm)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cmipci_free(cm); return -EBUSY; @@ -3398,7 +3398,7 @@ static int snd_cmipci_resume(struct pci_dev *pci) #endif /* CONFIG_PM */ static struct pci_driver driver = { - .name = "C-Media PCI", + .name = KBUILD_MODNAME, .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .remove = __devexit_p(snd_cmipci_remove), diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 6772070ed492..07f04e390aa1 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1382,7 +1382,7 @@ static int __devinit snd_cs4281_create(struct snd_card *card, } if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED, - "CS4281", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cs4281_free(chip); return -ENOMEM; @@ -2085,7 +2085,7 @@ static int cs4281_resume(struct pci_dev *pci) #endif /* CONFIG_PM */ static struct pci_driver driver = { - .name = "CS4281", + .name = KBUILD_MODNAME, .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .remove = __devexit_p(snd_cs4281_remove), diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 767fa7f06cd0..1af95559aaaa 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -162,7 +162,7 @@ static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Sound Fusion CS46xx", + .name = KBUILD_MODNAME, .id_table = snd_cs46xx_ids, .probe = snd_card_cs46xx_probe, .remove = __devexit_p(snd_card_cs46xx_remove), diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index aad37082cb6e..9546bf07f0d1 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3835,7 +3835,7 @@ int __devinit snd_cs46xx_create(struct snd_card *card, } if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_SHARED, - "CS46XX", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cs46xx_free(chip); return -EBUSY; diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index bc07e275d4d4..a4669346d146 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -285,7 +285,7 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci, } static struct pci_driver driver = { - .name = "CS5530_Audio", + .name = KBUILD_MODNAME, .id_table = snd_cs5530_ids, .probe = snd_cs5530_probe, .remove = __devexit_p(snd_cs5530_remove), diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index afb803708416..10d22ed5fece 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -311,7 +311,7 @@ static int __devinit snd_cs5535audio_create(struct snd_card *card, cs5535au->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_cs5535audio_interrupt, - IRQF_SHARED, "CS5535 Audio", cs5535au)) { + IRQF_SHARED, KBUILD_MODNAME, cs5535au)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto sndfail; @@ -395,7 +395,7 @@ static void __devexit snd_cs5535audio_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = DRIVER_NAME, + .name = KBUILD_MODNAME, .id_table = snd_cs5535audio_ids, .probe = snd_cs5535audio_probe, .remove = __devexit_p(snd_cs5535audio_remove), diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h index e0394e3996e8..ca501ba03d64 100644 --- a/sound/pci/ctxfi/ct20k2reg.h +++ b/sound/pci/ctxfi/ct20k2reg.h @@ -55,6 +55,7 @@ /* GPIO Registers */ #define GPIO_DATA 0x1B7020 #define GPIO_CTRL 0x1B7024 +#define GPIO_EXT_DATA 0x1B70A0 /* Virtual memory registers */ #define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 13f33c0719d3..d8a4423539ce 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -18,7 +18,6 @@ #include "ctatc.h" #include "ctpcm.h" #include "ctmixer.h" -#include "cthardware.h" #include "ctsrc.h" #include "ctamixer.h" #include "ctdaio.h" @@ -30,7 +29,6 @@ #include <sound/asoundef.h> #define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ -#define DAIONUM 7 #define MAX_MULTI_CHN 8 #define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ @@ -53,6 +51,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, "SB0760", CTSB0760), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270, + "SB1270", CTSB1270), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, @@ -75,6 +75,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = { [CTSB0760] = "SB076x", [CTHENDRIX] = "Hendrix", [CTSB0880] = "SB0880", + [CTSB1270] = "SB1270", [CT20K2_UNKNOWN] = "Unknown", }; @@ -459,12 +460,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, apcm->substream->runtime->rate); *n_srcc = 0; - if (1 == atc->msr) { + if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */ *n_srcc = apcm->substream->runtime->channels; conf[0].pitch = pitch; conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; conf[0].vo = 1; - } else if (2 == atc->msr) { + } else if (2 <= atc->msr) { if (0x8000000 < pitch) { /* Need two-stage SRCs, SRCIMPs and * AMIXERs for converting format */ @@ -970,11 +971,39 @@ static int atc_select_mic_in(struct ct_atc *atc) return 0; } -static int atc_have_digit_io_switch(struct ct_atc *atc) +static struct capabilities atc_capabilities(struct ct_atc *atc) { struct hw *hw = atc->hw; - return hw->have_digit_io_switch(hw); + return hw->capabilities(hw); +} + +static int atc_output_switch_get(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->output_switch_get(hw); +} + +static int atc_output_switch_put(struct ct_atc *atc, int position) +{ + struct hw *hw = atc->hw; + + return hw->output_switch_put(hw, position); +} + +static int atc_mic_source_switch_get(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->mic_source_switch_get(hw); +} + +static int atc_mic_source_switch_put(struct ct_atc *atc, int position) +{ + struct hw *hw = atc->hw; + + return hw->mic_source_switch_put(hw, position); } static int atc_select_digit_io(struct ct_atc *atc) @@ -1045,6 +1074,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) return atc_daio_unmute(atc, state, LINEIM); } +static int atc_mic_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, MIC); +} + static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) { return atc_daio_unmute(atc, state, SPDIFOO); @@ -1331,17 +1365,20 @@ static int atc_get_resources(struct ct_atc *atc) struct srcimp_mgr *srcimp_mgr; struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; - int err, i; + int err, i, num_srcs, num_daios; - atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); + num_daios = ((atc->model == CTSB1270) ? 8 : 7); + num_srcs = ((atc->model == CTSB1270) ? 6 : 4); + + atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL); if (!atc->daios) return -ENOMEM; - atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); if (!atc->srcs) return -ENOMEM; - atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); if (!atc->srcimps) return -ENOMEM; @@ -1351,8 +1388,9 @@ static int atc_get_resources(struct ct_atc *atc) daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; da_desc.msr = atc->msr; - for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { - da_desc.type = i; + for (i = 0, atc->n_daio = 0; i < num_daios; i++) { + da_desc.type = (atc->model != CTSB073X) ? i : + ((i == SPDIFIO) ? SPDIFI1 : i); err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1362,23 +1400,12 @@ static int atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - if (atc->model == CTSB073X) - da_desc.type = SPDIFI1; - else - da_desc.type = SPDIFIO; - err = daio_mgr->get_daio(daio_mgr, &da_desc, - (struct daio **)&atc->daios[i]); - if (err) { - printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n"); - return err; - } - atc->n_daio++; src_mgr = atc->rsc_mgrs[SRC]; src_dsc.multi = 1; src_dsc.msr = atc->msr; src_dsc.mode = ARCRW; - for (i = 0, atc->n_src = 0; i < (2*2); i++) { + for (i = 0, atc->n_src = 0; i < num_srcs; i++) { err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&atc->srcs[i]); if (err) @@ -1388,8 +1415,8 @@ static int atc_get_resources(struct ct_atc *atc) } srcimp_mgr = atc->rsc_mgrs[SRCIMP]; - srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ - for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { + srcimp_dsc.msr = 8; + for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) { err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, (struct srcimp **)&atc->srcimps[i]); if (err) @@ -1397,15 +1424,6 @@ static int atc_get_resources(struct ct_atc *atc) atc->n_srcimp++; } - srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ - for (i = 0; i < (2*1); i++) { - err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, - (struct srcimp **)&atc->srcimps[2*1+i]); - if (err) - return err; - - atc->n_srcimp++; - } sum_mgr = atc->rsc_mgrs[SUM]; sum_dsc.msr = atc->msr; @@ -1488,6 +1506,18 @@ static void atc_connect_resources(struct ct_atc *atc) src = atc->srcs[3]; mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + if (atc->model == CTSB1270) { + /* Titanium HD has a dedicated ADC for the Mic. */ + dai = container_of(atc->daios[MIC], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[4], + (struct srcimp **)&atc->srcimps[4]); + src = atc->srcs[4]; + mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); + src = atc->srcs[5]; + mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); + } + dai = container_of(atc->daios[SPDIFIO], struct dai, daio); atc_connect_dai(atc->rsc_mgrs[SRC], dai, (struct src **)&atc->srcs[0], @@ -1606,12 +1636,17 @@ static struct ct_atc atc_preset __devinitdata = { .line_clfe_unmute = atc_line_clfe_unmute, .line_rear_unmute = atc_line_rear_unmute, .line_in_unmute = atc_line_in_unmute, + .mic_unmute = atc_mic_unmute, .spdif_out_unmute = atc_spdif_out_unmute, .spdif_in_unmute = atc_spdif_in_unmute, .spdif_out_get_status = atc_spdif_out_get_status, .spdif_out_set_status = atc_spdif_out_set_status, .spdif_out_passthru = atc_spdif_out_passthru, - .have_digit_io_switch = atc_have_digit_io_switch, + .capabilities = atc_capabilities, + .output_switch_get = atc_output_switch_get, + .output_switch_put = atc_output_switch_put, + .mic_source_switch_get = atc_mic_source_switch_get, + .mic_source_switch_put = atc_mic_source_switch_put, #ifdef CONFIG_PM .suspend = atc_suspend, .resume = atc_resume, diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 7167c0185d52..3a0def656af0 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -25,6 +25,7 @@ #include <sound/core.h> #include "ctvmem.h" +#include "cthardware.h" #include "ctresource.h" enum CTALSADEVS { /* Types of alsa devices */ @@ -115,12 +116,17 @@ struct ct_atc { int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); + int (*mic_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); - int (*have_digit_io_switch)(struct ct_atc *atc); + struct capabilities (*capabilities)(struct ct_atc *atc); + int (*output_switch_get)(struct ct_atc *atc); + int (*output_switch_put)(struct ct_atc *atc, int position); + int (*mic_source_switch_get)(struct ct_atc *atc); + int (*mic_source_switch_put)(struct ct_atc *atc, int position); /* Don't touch! Used for internal object. */ void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 47d9ea97de02..0c00eb4088ef 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -22,20 +22,9 @@ #include <linux/slab.h> #include <linux/kernel.h> -#define DAIO_RESOURCE_NUM NUM_DAIOTYP #define DAIO_OUT_MAX SPDIFOO -union daio_usage { - struct { - unsigned short lineo1:1; - unsigned short lineo2:1; - unsigned short lineo3:1; - unsigned short lineo4:1; - unsigned short spdifoo:1; - unsigned short lineim:1; - unsigned short spdifio:1; - unsigned short spdifi1:1; - } bf; +struct daio_usage { unsigned short data; }; @@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO3] = {.left = 0x50, .right = 0x51}, [LINEO4] = {.left = 0x70, .right = 0x71}, [LINEIM] = {.left = 0x45, .right = 0xc5}, + [MIC] = {.left = 0x55, .right = 0xd5}, [SPDIFOO] = {.left = 0x00, .right = 0x01}, [SPDIFIO] = {.left = 0x05, .right = 0x85}, }; @@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) case LINEO3: return 5; case LINEO4: return 6; case LINEIM: return 4; + case MIC: return 5; default: return -EINVAL; } default: @@ -519,17 +510,17 @@ static int dai_rsc_uninit(struct dai *dai) static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) { - if (((union daio_usage *)mgr->rscs)->data & (0x1 << type)) + if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type)) return -ENOENT; - ((union daio_usage *)mgr->rscs)->data |= (0x1 << type); + ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type); return 0; } static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) { - ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type); + ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type); return 0; } @@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) if (!daio_mgr) return -ENOMEM; - err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); + err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw); if (err) goto error1; diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index 0f52ce571ee8..85ccb6ee1ab4 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -33,6 +33,7 @@ enum DAIOTYP { SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */ LINEIM, SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ + MIC, /* Dedicated mic on Titanium HD */ SPDIFI1, /* S/PDIF In on internal Drive Bay */ NUM_DAIOTYP }; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index af55405f5dec..908315bec3b4 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -39,6 +39,7 @@ enum CTCARDS { CT20K2_MODEL_FIRST = CTSB0760, CTHENDRIX, CTSB0880, + CTSB1270, CT20K2_UNKNOWN, NUM_CTCARDS /* This should always be the last */ }; @@ -60,6 +61,13 @@ struct card_conf { unsigned int msr; /* master sample rate in rsrs */ }; +struct capabilities { + unsigned int digit_io_switch:1; + unsigned int dedicated_mic:1; + unsigned int output_switch:1; + unsigned int mic_source_switch:1; +}; + struct hw { int (*card_init)(struct hw *hw, struct card_conf *info); int (*card_stop)(struct hw *hw); @@ -70,7 +78,11 @@ struct hw { #endif int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); int (*select_adc_source)(struct hw *hw, enum ADCSRC source); - int (*have_digit_io_switch)(struct hw *hw); + struct capabilities (*capabilities)(struct hw *hw); + int (*output_switch_get)(struct hw *hw); + int (*output_switch_put)(struct hw *hw, int position); + int (*mic_source_switch_get)(struct hw *hw); + int (*mic_source_switch_put)(struct hw *hw, int position); /* SRC operations */ int (*src_rsc_get_ctrl_blk)(void **rblk); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index a5c957db5cea..a7df19791f5a 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1777,10 +1777,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) return adc_init_SBx(hw, info->input, info->mic20db); } -static int hw_have_digit_io_switch(struct hw *hw) +static struct capabilities hw_capabilities(struct hw *hw) { + struct capabilities cap; + /* SB073x and Vista compatible cards have no digit IO switch */ - return !(hw->model == CTSB073X || hw->model == CTUAA); + cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA); + cap.dedicated_mic = 0; + cap.output_switch = 0; + cap.mic_source_switch = 0; + + return cap; } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1933,7 +1940,7 @@ static int hw_card_start(struct hw *hw) if (hw->irq < 0) { err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, - "ctxfi", hw); + KBUILD_MODNAME, hw); if (err < 0) { printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); goto error2; @@ -2172,7 +2179,7 @@ static struct hw ct20k1_preset __devinitdata = { .pll_init = hw_pll_init, .is_adc_source_selected = hw_is_adc_input_selected, .select_adc_source = hw_adc_input_select, - .have_digit_io_switch = hw_have_digit_io_switch, + .capabilities = hw_capabilities, #ifdef CONFIG_PM .suspend = hw_suspend, .resume = hw_resume, diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 5364164674e4..d6c54b524bfa 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -8,7 +8,7 @@ * @File cthw20k2.c * * @Brief - * This file contains the implementation of hardware access methord for 20k2. + * This file contains the implementation of hardware access method for 20k2. * * @Author Liu Chun * @Date May 14 2008 @@ -38,6 +38,8 @@ struct hw20k2 { unsigned char dev_id; unsigned char addr_size; unsigned char data_size; + + int mic_source; }; static u32 hw_read_20kx(struct hw *hw, u32 reg); @@ -1163,7 +1165,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else if (2 == info->msr) { - hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + if (hw->model != CTSB1270) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + } else { + /* PCM4220 on Titanium HD is different. */ + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111); + } /* Specify all playing 96khz * EA [0] - Enabled * RTA [4:5] - 96kHz @@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) * RTD [28:29] - 96kHz */ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else if ((4 == info->msr) && (hw->model == CTSB1270)) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111); + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); return -EINVAL; @@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) for (i = 0; i < 8; i++) { if (i <= 3) { + /* This comment looks wrong since loop is over 4 */ + /* channels and emu20k2 supports 4 spdif IOs. */ /* 1st 3 channels are SPDIFs (SB0960) */ if (i == 3) data = 0x1001001; @@ -1206,12 +1219,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); } else { + /* Again, loop is over 4 channels not 5. */ /* Next 5 channels are I2S (SB0960) */ data = 0x11; hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); if (2 == info->msr) { /* Four channels per sample period */ data |= 0x1000; + } else if (4 == info->msr) { + /* FIXME: check this against the chip spec */ + data |= 0x2000; } hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data); } @@ -1299,21 +1316,18 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) pllenb = 0xB; hw_write_20kx(hw, PLL_ENB, pllenb); - pllctl = 0x20D00000; - set_field(&pllctl, PLLCTL_FD, 16 - 4); + pllctl = 0x20C00000; + set_field(&pllctl, PLLCTL_B, 0); + set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 4 : 147 - 4); + set_field(&pllctl, PLLCTL_RD, 48000 == rsr ? 1 - 1 : 10 - 1); hw_write_20kx(hw, PLL_CTL, pllctl); mdelay(40); + pllctl = hw_read_20kx(hw, PLL_CTL); - set_field(&pllctl, PLLCTL_B, 0); - if (48000 == rsr) { - set_field(&pllctl, PLLCTL_FD, 16 - 2); - set_field(&pllctl, PLLCTL_RD, 1 - 1); /* 3000*16/1 = 48000 */ - } else { /* 44100 */ - set_field(&pllctl, PLLCTL_FD, 147 - 2); - set_field(&pllctl, PLLCTL_RD, 10 - 1); /* 3000*147/10 = 44100 */ - } + set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 2 : 147 - 2); hw_write_20kx(hw, PLL_CTL, pllctl); mdelay(40); + for (i = 0; i < 1000; i++) { pllstat = hw_read_20kx(hw, PLL_STAT); if (get_field(pllstat, PLLSTAT_PD)) @@ -1557,7 +1571,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); hw20k2_i2c_wait_data_ready(hw); - /* Dummy write to trigger the write oprtation */ + /* Dummy write to trigger the write operation */ hw_write_20kx(hw, I2C_IF_WDATA, 0); hw20k2_i2c_wait_data_ready(hw); @@ -1568,6 +1582,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) return 0; } +static void hw_dac_stop(struct hw *hw) +{ + u32 data; + data = hw_read_20kx(hw, GPIO_DATA); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); + mdelay(10); +} + +static void hw_dac_start(struct hw *hw) +{ + u32 data; + data = hw_read_20kx(hw, GPIO_DATA); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); + mdelay(50); +} + +static void hw_dac_reset(struct hw *hw) +{ + hw_dac_stop(hw); + hw_dac_start(hw); +} + static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { int err; @@ -1594,6 +1632,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) 0x00000000 /* Vol Control B4 */ }; + if (hw->model == CTSB1270) { + hw_dac_stop(hw); + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~0x0600; + if (1 == info->msr) + data |= 0x0000; /* Single Speed Mode 0-50kHz */ + else if (2 == info->msr) + data |= 0x0200; /* Double Speed Mode 50-100kHz */ + else + data |= 0x0600; /* Quad Speed Mode 100-200kHz */ + hw_write_20kx(hw, GPIO_DATA, data); + hw_dac_start(hw); + return 0; + } + /* Set DAC reset bit as output */ data = hw_read_20kx(hw, GPIO_CTRL); data |= 0x02; @@ -1606,22 +1659,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) for (i = 0; i < 2; i++) { /* Reset DAC twice just in-case the chip * didn't initialized properly */ - data = hw_read_20kx(hw, GPIO_DATA); - /* GPIO data bit 1 */ - data &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(10); - data |= 0x2; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(50); - - /* Reset the 2nd time */ - data &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(10); - data |= 0x2; - hw_write_20kx(hw, GPIO_DATA, data); - mdelay(50); + hw_dac_reset(hw); + hw_dac_reset(hw); if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) continue; @@ -1725,7 +1764,11 @@ End: static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { u32 data; - + if (hw->model == CTSB1270) { + /* Titanium HD has two ADC chips, one for line in and one */ + /* for MIC. We don't need to switch the ADC input. */ + return 1; + } data = hw_read_20kx(hw, GPIO_DATA); switch (type) { case ADC_MICIN: @@ -1742,35 +1785,47 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) #define MIC_BOOST_0DB 0xCF #define MIC_BOOST_STEPS_PER_DB 2 -#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB) + +static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db) +{ + u32 adcmc, gain; + + if (input > 3) + input = 3; + + adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */ + + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc), + MAKE_WM8775_DATA(adcmc)); + + if (gain_in_db < -103) + gain_in_db = -103; + if (gain_in_db > 24) + gain_in_db = 24; + + gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB; + + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain), + MAKE_WM8775_DATA(gain)); + /* ...so there should be no need for the following. */ + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain), + MAKE_WM8775_DATA(gain)); +} static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { u32 data; - data = hw_read_20kx(hw, GPIO_DATA); switch (type) { case ADC_MICIN: data |= (0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), - MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ + hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */ break; case ADC_LINEIN: data &= ~(0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), - MAKE_WM8775_DATA(0x102)); /* Line-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ + hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */ break; default: break; @@ -1782,7 +1837,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { int err; - u32 mux = 2, data, ctl; + u32 data, ctl; /* Set ADC reset bit as output */ data = hw_read_20kx(hw, GPIO_CTRL); @@ -1796,19 +1851,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) goto error; } - /* Make ADC in normal operation */ + /* Reset the ADC (reset is active low). */ data = hw_read_20kx(hw, GPIO_DATA); data &= ~(0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); + + if (hw->model == CTSB1270) { + /* Set up the PCM4220 ADC on Titanium HD */ + data &= ~0x0C; + if (1 == info->msr) + data |= 0x00; /* Single Speed Mode 32-50kHz */ + else if (2 == info->msr) + data |= 0x08; /* Double Speed Mode 50-108kHz */ + else + data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */ + hw_write_20kx(hw, GPIO_DATA, data); + } + mdelay(10); + /* Return the ADC to normal operation. */ data |= (0x1 << 15); hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); + /* I2C write to register offset 0x0B to set ADC LRCLK polarity */ + /* invert bit, interface format to I2S, word length to 24-bit, */ + /* enable ADC high pass filter. Fixes bug 5323? */ + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26), + MAKE_WM8775_DATA(0x26)); + /* Set the master mode (256fs) */ if (1 == info->msr) { + /* slave mode, 128x oversampling 256fs */ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), MAKE_WM8775_DATA(0x02)); - } else if (2 == info->msr) { + } else if ((2 == info->msr) || (4 == info->msr)) { + /* slave mode, 64x oversampling, 256fs */ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { @@ -1818,55 +1896,113 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) goto error; } - /* Configure GPIO bit 14 change to line-in/mic-in */ - ctl = hw_read_20kx(hw, GPIO_CTRL); - ctl |= 0x1 << 14; - hw_write_20kx(hw, GPIO_CTRL, ctl); - - /* Check using Mic-in or Line-in */ - data = hw_read_20kx(hw, GPIO_DATA); - - if (mux == 1) { - /* Configures GPIO data to select Mic-in */ - data |= 0x1 << 14; - hw_write_20kx(hw, GPIO_DATA, data); - - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), - MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - hw20k2_i2c_write(hw, - MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), - MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ - } else if (mux == 2) { - /* Configures GPIO data to select Line-in */ - data &= ~(0x1 << 14); - hw_write_20kx(hw, GPIO_DATA, data); - - /* Setup ADC */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), - MAKE_WM8775_DATA(0x102)); /* Line-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), - MAKE_WM8775_DATA(0xCF)); /* No boost */ + if (hw->model != CTSB1270) { + /* Configure GPIO bit 14 change to line-in/mic-in */ + ctl = hw_read_20kx(hw, GPIO_CTRL); + ctl |= 0x1 << 14; + hw_write_20kx(hw, GPIO_CTRL, ctl); + hw_adc_input_select(hw, ADC_LINEIN); } else { - printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); - err = -EINVAL; - goto error; + hw_wm8775_input_select(hw, 0, 0); } return 0; - error: hw20k2_i2c_uninit(hw); return err; } -static int hw_have_digit_io_switch(struct hw *hw) +static struct capabilities hw_capabilities(struct hw *hw) { - return 0; + struct capabilities cap; + + cap.digit_io_switch = 0; + cap.dedicated_mic = hw->model == CTSB1270; + cap.output_switch = hw->model == CTSB1270; + cap.mic_source_switch = hw->model == CTSB1270; + + return cap; +} + +static int hw_output_switch_get(struct hw *hw) +{ + u32 data = hw_read_20kx(hw, GPIO_EXT_DATA); + + switch (data & 0x30) { + case 0x00: + return 0; + case 0x10: + return 1; + case 0x20: + return 2; + default: + return 3; + } +} + +static int hw_output_switch_put(struct hw *hw, int position) +{ + u32 data; + + if (position == hw_output_switch_get(hw)) + return 0; + + /* Mute line and headphones (intended for anti-pop). */ + data = hw_read_20kx(hw, GPIO_DATA); + data |= (0x03 << 11); + hw_write_20kx(hw, GPIO_DATA, data); + + data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30; + switch (position) { + case 0: + break; + case 1: + data |= 0x10; + break; + default: + data |= 0x20; + } + hw_write_20kx(hw, GPIO_EXT_DATA, data); + + /* Unmute line and headphones. */ + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~(0x03 << 11); + hw_write_20kx(hw, GPIO_DATA, data); + + return 1; +} + +static int hw_mic_source_switch_get(struct hw *hw) +{ + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + + return hw20k2->mic_source; +} + +static int hw_mic_source_switch_put(struct hw *hw, int position) +{ + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + + if (position == hw20k2->mic_source) + return 0; + + switch (position) { + case 0: + hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */ + break; + case 1: + hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */ + break; + case 2: + hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */ + break; + default: + return 0; + } + + hw20k2->mic_source = position; + + return 1; } static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id) @@ -1925,7 +2061,7 @@ static int hw_card_start(struct hw *hw) if (hw->irq < 0) { err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED, - "ctxfi", hw); + KBUILD_MODNAME, hw); if (err < 0) { printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); goto error2; @@ -2023,13 +2159,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) /* Reset all SRC pending interrupts */ hw_write_20kx(hw, SRC_IP, 0); - /* TODO: detect the card ID and configure GPIO accordingly. */ - /* Configures GPIO (0xD802 0x98028) */ - /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ - /* Configures GPIO (SB0880) */ - /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ - hw_write_20kx(hw, GPIO_CTRL, 0xD802); - + if (hw->model != CTSB1270) { + /* TODO: detect the card ID and configure GPIO accordingly. */ + /* Configures GPIO (0xD802 0x98028) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ + /* Configures GPIO (SB0880) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ + hw_write_20kx(hw, GPIO_CTRL, 0xD802); + } else { + hw_write_20kx(hw, GPIO_CTRL, 0x9E5F); + } /* Enable audio ring */ hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); @@ -2106,7 +2245,11 @@ static struct hw ct20k2_preset __devinitdata = { .pll_init = hw_pll_init, .is_adc_source_selected = hw_is_adc_input_selected, .select_adc_source = hw_adc_input_select, - .have_digit_io_switch = hw_have_digit_io_switch, + .capabilities = hw_capabilities, + .output_switch_get = hw_output_switch_get, + .output_switch_put = hw_output_switch_put, + .mic_source_switch_get = hw_mic_source_switch_get, + .mic_source_switch_put = hw_mic_source_switch_put, #ifdef CONFIG_PM .suspend = hw_suspend, .resume = hw_resume, diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index c3519ff42fbb..0cc13eeef8da 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL { MIXER_LINEIN_C_S, MIXER_MIC_C_S, MIXER_SPDIFI_C_S, - MIXER_LINEIN_P_S, MIXER_SPDIFO_P_S, - MIXER_SPDIFI_P_S, MIXER_WAVEF_P_S, MIXER_WAVER_P_S, MIXER_WAVEC_P_S, @@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_LINEIN_P] = { .ctl = 1, - .name = "Line-in Playback Volume", + .name = "Line Playback Volume", }, [MIXER_LINEIN_C] = { .ctl = 1, - .name = "Line-in Capture Volume", + .name = "Line Capture Volume", }, [MIXER_MIC_P] = { .ctl = 1, @@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_SPDIFI_P] = { .ctl = 1, - .name = "S/PDIF-in Playback Volume", + .name = "IEC958 Playback Volume", }, [MIXER_SPDIFI_C] = { .ctl = 1, - .name = "S/PDIF-in Capture Volume", + .name = "IEC958 Capture Volume", }, [MIXER_SPDIFO_P] = { .ctl = 1, - .name = "S/PDIF-out Playback Volume", + .name = "Digital Playback Volume", }, [MIXER_WAVEF_P] = { .ctl = 1, @@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { .ctl = 1, .name = "Surround Playback Volume", }, - [MIXER_PCM_C_S] = { .ctl = 1, .name = "PCM Capture Switch", }, [MIXER_LINEIN_C_S] = { .ctl = 1, - .name = "Line-in Capture Switch", + .name = "Line Capture Switch", }, [MIXER_MIC_C_S] = { .ctl = 1, @@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_SPDIFI_C_S] = { .ctl = 1, - .name = "S/PDIF-in Capture Switch", - }, - [MIXER_LINEIN_P_S] = { - .ctl = 1, - .name = "Line-in Playback Switch", + .name = "IEC958 Capture Switch", }, [MIXER_SPDIFO_P_S] = { .ctl = 1, - .name = "S/PDIF-out Playback Switch", - }, - [MIXER_SPDIFI_P_S] = { - .ctl = 1, - .name = "S/PDIF-in Playback Switch", + .name = "Digital Playback Switch", }, [MIXER_WAVEF_P_S] = { .ctl = 1, @@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); static void ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); +/* FIXME: this static looks like it would fail if more than one card was */ +/* installed. */ static struct snd_kcontrol *kctls[2] = {NULL}; static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) @@ -420,6 +411,77 @@ static struct snd_kcontrol_new vol_ctl = { .tlv = { .p = ct_vol_db_scale }, }; +static int output_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "FP Headphones", "Headphones", "Speakers" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int output_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc); + return 0; +} + +static int output_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]); +} + +static struct snd_kcontrol_new output_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output Playback Enum", + .info = output_switch_info, + .get = output_switch_get, + .put = output_switch_put, +}; + +static int mic_source_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Mic", "FP Mic", "Aux" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int mic_source_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc); + return 0; +} + +static int mic_source_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + return atc->mic_source_switch_put(atc, + ucontrol->value.enumerated.item[0]); +} + +static struct snd_kcontrol_new mic_source_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Source Capture Enum", + .info = mic_source_switch_info, + .get = mic_source_switch_get, + .put = mic_source_switch_put, +}; + static void do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) { @@ -465,6 +527,7 @@ do_digit_io_switch(struct ct_atc *atc, int state) static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) { struct ct_mixer *mixer = atc->mixer; + struct capabilities cap = atc->capabilities(atc); /* Do changes in mixer. */ if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { @@ -477,8 +540,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) } } /* Do changes out of mixer. */ - if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) - do_line_mic_switch(atc, type); + if (!cap.dedicated_mic && + (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) { + if (state) + do_line_mic_switch(atc, type); + atc->line_in_unmute(atc, state); + } else if (cap.dedicated_mic && (MIXER_LINEIN_C_S == type)) + atc->line_in_unmute(atc, state); + else if (cap.dedicated_mic && (MIXER_MIC_C_S == type)) + atc->mic_unmute(atc, state); + else if (MIXER_SPDIFI_C_S == type) + atc->spdif_in_unmute(atc, state); else if (MIXER_WAVEF_P_S == type) atc->line_front_unmute(atc, state); else if (MIXER_WAVES_P_S == type) @@ -487,12 +559,8 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state) atc->line_clfe_unmute(atc, state); else if (MIXER_WAVER_P_S == type) atc->line_rear_unmute(atc, state); - else if (MIXER_LINEIN_P_S == type) - atc->line_in_unmute(atc, state); else if (MIXER_SPDIFO_P_S == type) atc->spdif_out_unmute(atc, state); - else if (MIXER_SPDIFI_P_S == type) - atc->spdif_in_unmute(atc, state); else if (MIXER_DIGITAL_IO_S == type) do_digit_io_switch(atc, state); @@ -671,6 +739,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) { enum CTALSA_MIXER_CTL type; struct ct_atc *atc = mixer->atc; + struct capabilities cap = atc->capabilities(atc); int err; /* Create snd kcontrol instances on demand */ @@ -684,8 +753,8 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) } } - ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = - atc->have_digit_io_switch(atc); + ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = cap.digit_io_switch; + for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { if (ct_kcontrol_init_table[type].ctl) { swh_ctl.name = ct_kcontrol_init_table[type].name; @@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) if (err) return err; + if (cap.output_switch) { + err = ct_mixer_kcontrol_new(mixer, &output_ctl); + if (err) + return err; + } + + if (cap.mic_source_switch) { + err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl); + if (err) + return err; + } atc->line_front_unmute(atc, 1); set_switch_state(mixer, MIXER_WAVEF_P_S, 1); atc->line_surround_unmute(atc, 0); @@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) atc->spdif_out_unmute(atc, 0); set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); atc->line_in_unmute(atc, 0); - set_switch_state(mixer, MIXER_LINEIN_P_S, 0); + if (cap.dedicated_mic) + atc->mic_unmute(atc, 0); atc->spdif_in_unmute(atc, 0); - set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); - - set_switch_state(mixer, MIXER_PCM_C_S, 1); - set_switch_state(mixer, MIXER_LINEIN_C_S, 1); - set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); + set_switch_state(mixer, MIXER_PCM_C_S, 0); + set_switch_state(mixer, MIXER_LINEIN_C_S, 0); + set_switch_state(mixer, MIXER_SPDIFI_C_S, 0); return 0; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index f42e7e1a1074..b259aa03a3a9 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -80,11 +80,11 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "are 48000 and 44100, Value 48000 is assumed.\n"); reference_rate = 48000; } - if ((multiple != 1) && (multiple != 2)) { + if ((multiple != 1) && (multiple != 2) && (multiple != 4)) { printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", multiple); printk(KERN_ERR "ctxfi: The valid values for multiple are " - "1 and 2, Value 2 is assumed.\n"); + "1, 2 and 4, Value 2 is assumed.\n"); multiple = 2; } err = ct_atc_create(card, pci, reference_rate, multiple, @@ -143,7 +143,7 @@ static int ct_card_resume(struct pci_dev *pci) #endif static struct pci_driver ct_driver = { - .name = "SB-XFi", + .name = KBUILD_MODNAME, .id_table = ct_pci_dev_ids, .probe = ct_card_probe, .remove = __devexit_p(ct_card_remove), diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index fe7ad64dccd7..43c7e12bc05d 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -52,7 +52,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/darla20_dsp.fw"); diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index d1fd34b1a8e3..95b03306e026 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -56,7 +56,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/darla24_dsp.fw"); diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index 1dffdc54416d..8723c40183e6 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -64,7 +64,7 @@ #include <sound/initval.h> #include <sound/rawmidi.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 20763dd03fa0..d7306980d0f1 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1995,7 +1995,7 @@ static __devinit int snd_echo_create(struct snd_card *card, ioremap_nocache(chip->dsp_registers_phys, sz); if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, - ECHOCARD_NAME, chip)) { + KBUILD_MODNAME, chip)) { snd_echo_free(chip); snd_printk(KERN_ERR "cannot grab irq\n"); return -EBUSY; @@ -2286,7 +2286,7 @@ static int snd_echo_resume(struct pci_dev *pci) kfree(commpage_bak); if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, - ECHOCARD_NAME, chip)) { + KBUILD_MODNAME, chip)) { snd_echo_free(chip); snd_printk(KERN_ERR "cannot grab irq\n"); return -EBUSY; @@ -2327,7 +2327,7 @@ static void __devexit snd_echo_remove(struct pci_dev *pci) /* pci_driver definition */ static struct pci_driver driver = { - .name = "Echoaudio " ECHOCARD_NAME, + .name = KBUILD_MODNAME, .id_table = snd_echo_ids, .probe = snd_echo_probe, .remove = __devexit_p(snd_echo_remove), diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index 050e54aa693f..0058c67115df 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -56,7 +56,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/gina20_dsp.fw"); diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index 5748fc6d29d6..14e4925e76cc 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -62,7 +62,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 4ae5e35cb5f1..f416b154f146 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -54,7 +54,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 3550715bab1c..e594a3b2766e 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -54,7 +54,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c index 19b191fd0120..f0d00bfceee5 100644 --- a/sound/pci/echoaudio/indigodjx.c +++ b/sound/pci/echoaudio/indigodjx.c @@ -54,7 +54,7 @@ #include <sound/pcm_params.h> #include <sound/asoundef.h> #include <sound/initval.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index a9fcedf317a4..1af0037304c6 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -55,7 +55,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c index bcdfac63212c..0b51163452b5 100644 --- a/sound/pci/echoaudio/indigoiox.c +++ b/sound/pci/echoaudio/indigoiox.c @@ -55,7 +55,7 @@ #include <sound/pcm_params.h> #include <sound/asoundef.h> #include <sound/initval.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index d3a98c5dac86..3f63ab8dfff3 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -62,7 +62,7 @@ #include <sound/initval.h> #include <sound/rawmidi.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/layla20_dsp.fw"); diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index 2a1dca6dce17..283137244472 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -64,7 +64,7 @@ #include <sound/initval.h> #include <sound/rawmidi.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index 9cdf14cfdd74..eddaeb4da50e 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -63,7 +63,7 @@ #include <sound/initval.h> #include <sound/rawmidi.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 1047be405ebe..0364011c237d 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -60,7 +60,7 @@ #include <sound/asoundef.h> #include <sound/initval.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "echoaudio.h" MODULE_FIRMWARE("ea/loader_dsp.fw"); diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index aff8387c45cf..a9c45d2cdb13 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -264,7 +264,7 @@ static int snd_emu10k1_resume(struct pci_dev *pci) #endif static struct pci_driver driver = { - .name = "EMU10K1_Audigy", + .name = KBUILD_MODNAME, .id_table = snd_emu10k1_ids, .probe = snd_card_emu10k1_probe, .remove = __devexit_p(snd_card_emu10k1_remove), diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 15f0161ce4a2..fcd4935766b2 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1912,7 +1912,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card, /* irq handler must be registered after I/O ports are activated */ if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED, - "EMU10K1", emu)) { + KBUILD_MODNAME, emu)) { err = -EBUSY; goto error; } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 0c701e4ec8a5..d4fde1b4b093 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -925,7 +925,7 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card, } if (request_irq(pci->irq, snd_emu10k1x_interrupt, - IRQF_SHARED, "EMU10K1X", chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq); snd_emu10k1x_free(chip); return -EBUSY; @@ -1613,7 +1613,7 @@ MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids); // pci_driver definition static struct pci_driver driver = { - .name = "EMU10K1X", + .name = KBUILD_MODNAME, .id_table = snd_emu10k1x_ids, .probe = snd_emu10k1x_probe, .remove = __devexit_p(snd_emu10k1x_remove), diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 863eafea691f..f02e2f8d7122 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2120,7 +2120,7 @@ static int __devinit snd_ensoniq_create(struct snd_card *card, } ensoniq->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_SHARED, - "Ensoniq AudioPCI", ensoniq)) { + KBUILD_MODNAME, ensoniq)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ensoniq_free(ensoniq); return -EBUSY; @@ -2489,7 +2489,7 @@ static void __devexit snd_audiopci_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = DRIVER_NAME, + .name = KBUILD_MODNAME, .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .remove = __devexit_p(snd_audiopci_remove), diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 553b75217259..26a5a2f25d4b 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1514,7 +1514,7 @@ static int es1938_resume(struct pci_dev *pci) } if (request_irq(pci->irq, snd_es1938_interrupt, - IRQF_SHARED, "ES1938", chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { printk(KERN_ERR "es1938: unable to grab IRQ %d, " "disabling device\n", pci->irq); snd_card_disconnect(card); @@ -1636,7 +1636,7 @@ static int __devinit snd_es1938_create(struct snd_card *card, chip->mpu_port = pci_resource_start(pci, 3); chip->game_port = pci_resource_start(pci, 4); if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED, - "ES1938", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_es1938_free(chip); return -EBUSY; @@ -1882,7 +1882,7 @@ static void __devexit snd_es1938_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ESS ES1938 (Solo-1)", + .name = KBUILD_MODNAME, .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .remove = __devexit_p(snd_es1938_remove), diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index ab0a6156a704..99ea9320c6b5 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -554,9 +554,8 @@ struct es1968 { #else struct snd_kcontrol *master_switch; /* for h/w volume control */ struct snd_kcontrol *master_volume; - spinlock_t ac97_lock; - struct tasklet_struct hwvol_tq; #endif + struct work_struct hwvol_work; #ifdef CONFIG_SND_ES1968_RADIO struct snd_tea575x tea; @@ -646,38 +645,23 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip) static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct es1968 *chip = ac97->private_data; -#ifndef CONFIG_SND_ES1968_INPUT - unsigned long flags; -#endif snd_es1968_ac97_wait(chip); /* Write the bus */ -#ifndef CONFIG_SND_ES1968_INPUT - spin_lock_irqsave(&chip->ac97_lock, flags); -#endif outw(val, chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ outb(reg, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ -#ifndef CONFIG_SND_ES1968_INPUT - spin_unlock_irqrestore(&chip->ac97_lock, flags); -#endif } static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { u16 data = 0; struct es1968 *chip = ac97->private_data; -#ifndef CONFIG_SND_ES1968_INPUT - unsigned long flags; -#endif snd_es1968_ac97_wait(chip); -#ifndef CONFIG_SND_ES1968_INPUT - spin_lock_irqsave(&chip->ac97_lock, flags); -#endif outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ @@ -685,9 +669,6 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short data = inw(chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ } -#ifndef CONFIG_SND_ES1968_INPUT - spin_unlock_irqrestore(&chip->ac97_lock, flags); -#endif return data; } @@ -1904,13 +1885,10 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es) (without wrap around) in response to volume button presses and then generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 of a byte wide register. The meaning of bits 0 and 4 is unknown. */ -static void es1968_update_hw_volume(unsigned long private_data) +static void es1968_update_hw_volume(struct work_struct *work) { - struct es1968 *chip = (struct es1968 *) private_data; + struct es1968 *chip = container_of(work, struct es1968, hwvol_work); int x, val; -#ifndef CONFIG_SND_ES1968_INPUT - unsigned long flags; -#endif /* Figure out which volume control button was pushed, based on differences from the default register @@ -1929,18 +1907,11 @@ static void es1968_update_hw_volume(unsigned long private_data) if (! chip->master_switch || ! chip->master_volume) return; - /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ - spin_lock_irqsave(&chip->ac97_lock, flags); - val = chip->ac97->regs[AC97_MASTER]; + val = snd_ac97_read(chip->ac97, AC97_MASTER); switch (x) { case 0x88: /* mute */ val ^= 0x8000; - chip->ac97->regs[AC97_MASTER] = val; - outw(val, chip->io_port + ESM_AC97_DATA); - outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_switch->id); break; case 0xaa: /* volume up */ @@ -1948,11 +1919,6 @@ static void es1968_update_hw_volume(unsigned long private_data) val--; if ((val & 0x7f00) > 0) val -= 0x0100; - chip->ac97->regs[AC97_MASTER] = val; - outw(val, chip->io_port + ESM_AC97_DATA); - outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_volume->id); break; case 0x66: /* volume down */ @@ -1960,14 +1926,11 @@ static void es1968_update_hw_volume(unsigned long private_data) val++; if ((val & 0x7f00) < 0x1f00) val += 0x0100; - chip->ac97->regs[AC97_MASTER] = val; - outw(val, chip->io_port + ESM_AC97_DATA); - outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_volume->id); break; } - spin_unlock_irqrestore(&chip->ac97_lock, flags); + if (snd_ac97_update(chip->ac97, AC97_MASTER, val)) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); #else if (!chip->input_dev) return; @@ -2013,11 +1976,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); if (event & ESM_HWVOL_IRQ) -#ifdef CONFIG_SND_ES1968_INPUT - es1968_update_hw_volume((unsigned long)chip); -#else - tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */ -#endif + schedule_work(&chip->hwvol_work); /* else ack 'em all, i imagine */ outb(0xFF, chip->io_port + 0x1A); @@ -2426,6 +2385,7 @@ static int es1968_suspend(struct pci_dev *pci, pm_message_t state) return 0; chip->in_suspend = 1; + cancel_work_sync(&chip->hwvol_work); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); @@ -2638,6 +2598,7 @@ static struct snd_tea575x_ops snd_es1968_tea_ops = { static int snd_es1968_free(struct es1968 *chip) { + cancel_work_sync(&chip->hwvol_work); #ifdef CONFIG_SND_ES1968_INPUT if (chip->input_dev) input_unregister_device(chip->input_dev); @@ -2728,10 +2689,7 @@ static int __devinit snd_es1968_create(struct snd_card *card, INIT_LIST_HEAD(&chip->buf_list); INIT_LIST_HEAD(&chip->substream_list); mutex_init(&chip->memory_mutex); -#ifndef CONFIG_SND_ES1968_INPUT - spin_lock_init(&chip->ac97_lock); - tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); -#endif + INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume); chip->card = card; chip->pci = pci; chip->irq = -1; @@ -2746,7 +2704,7 @@ static int __devinit snd_es1968_create(struct snd_card *card, } chip->io_port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED, - "ESS Maestro", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_es1968_free(chip); return -EBUSY; @@ -2925,7 +2883,7 @@ static void __devexit snd_es1968_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ES1968 (ESS Maestro)", + .name = KBUILD_MODNAME, .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .remove = __devexit_p(snd_es1968_remove), diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index a7ec7030cf87..f9123f09e83e 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1199,7 +1199,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, chip->port = pci_resource_start(pci, 0); if ((tea575x_tuner & TUNER_ONLY) == 0) { if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED, - "FM801", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); snd_fm801_free(chip); return -EBUSY; @@ -1394,7 +1394,7 @@ static int snd_fm801_resume(struct pci_dev *pci) #endif static struct pci_driver driver = { - .name = "FM801", + .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .remove = __devexit_p(snd_card_fm801_remove), diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 0ea5cc60ac78..bb7e102d6726 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -14,6 +14,19 @@ menuconfig SND_HDA_INTEL if SND_HDA_INTEL +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 64 + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. + config SND_HDA_HWDEP bool "Build hwdep interface for HD-audio driver" select SND_HWDEP @@ -83,6 +96,19 @@ config SND_HDA_CODEC_REALTEK snd-hda-codec-realtek. This module is automatically loaded at probing. +config SND_HDA_ENABLE_REALTEK_QUIRKS + bool "Build static quirks for Realtek codecs" + depends on SND_HDA_CODEC_REALTEK + default y + help + Say Y here to build the static quirks codes for Realtek codecs. + If you need the "model" preset that the default BIOS auto-parser + can't handle, turn this option on. + + If your device works with model=auto option, basically you don't + need the quirk code. By turning this off, you can reduce the + module size quite a lot. + config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y @@ -171,6 +197,19 @@ config SND_HDA_CODEC_CA0110 snd-hda-codec-ca0110. This module is automatically loaded at probing. +config SND_HDA_CODEC_CA0132 + bool "Build Creative CA0132 codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Creative CA0132 codec support in + snd-hda-intel driver. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-ca0132. + This module is automatically loaded at probing. + config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y @@ -204,6 +243,7 @@ config SND_HDA_GENERIC config SND_HDA_POWER_SAVE bool "Aggressive power-saving on HD-audio" + depends on PM help Say Y here to enable more aggressive power-saving mode on HD-audio driver. The power-saving timeout can be configured diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 17ef3658f34b..87365d5ea2a9 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o +snd-hda-codec-ca0132-objs := patch_ca0132.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o @@ -42,6 +43,9 @@ endif ifdef CONFIG_SND_HDA_CODEC_CA0110 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o endif +ifdef CONFIG_SND_HDA_CODEC_CA0132 +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.o +endif ifdef CONFIG_SND_HDA_CODEC_CONEXANT obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o endif diff --git a/sound/pci/hda/alc260_quirks.c b/sound/pci/hda/alc260_quirks.c new file mode 100644 index 000000000000..21ec2cb100b0 --- /dev/null +++ b/sound/pci/hda/alc260_quirks.c @@ -0,0 +1,1272 @@ +/* + * ALC260 quirk models + * included by patch_realtek.c + */ + +/* ALC260 models */ +enum { + ALC260_AUTO, + ALC260_BASIC, + ALC260_HP, + ALC260_HP_DC7600, + ALC260_HP_3013, + 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 + * HP: base_output + input + capture_alt + * HP_3013: hp_3013 + input + 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 */ +}; + +/* update HP, line and mono out pins according to the master switch */ +static void alc260_hp_master_update(struct hda_codec *codec) +{ + update_speakers(codec); +} + +static int alc260_hp_master_sw_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; + *ucontrol->value.integer.value = !spec->master_mute; + return 0; +} + +static int alc260_hp_master_sw_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 val = !*ucontrol->value.integer.value; + + if (val == spec->master_mute) + return 0; + spec->master_mute = val; + alc260_hp_master_update(codec); + return 1; +} + +static const struct snd_kcontrol_new alc260_hp_output_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + }, + 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("Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc260_hp_unsol_verbs[] = { + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {}, +}; + +static void alc260_hp_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x0f; + spec->autocfg.speaker_pins[0] = 0x10; + spec->autocfg.speaker_pins[1] = 0x11; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc260_hp_3013_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + }, + HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static void alc260_hp_3013_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x10; + spec->autocfg.speaker_pins[1] = 0x11; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct hda_bind_ctls alc260_dc7600_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc260_dc7600_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol), + HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct hda_verb alc260_hp_3013_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {}, +}; + +static void alc260_hp_3012_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x10; + spec->autocfg.speaker_pins[0] = 0x0f; + spec->autocfg.speaker_pins[1] = 0x11; + spec->autocfg.speaker_pins[2] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* 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)}, + { } +}; + +#if 0 /* should be identical with alc260_init_verbs? */ +static const struct hda_verb alc260_hp_init_verbs[] = { + /* Headphone and output */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + /* mono output */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Line-2 pin widget for output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* unmute amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute Line-Out mixer amp left and right (volume = 0) */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute HP mixer amp left and right (volume = 0) */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* 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 */ + /* Unmute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + { } +}; +#endif + +static const struct hda_verb alc260_hp_3013_init_verbs[] = { + /* Line out and output */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* mono output */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic1 (rear panel) pin widget for input and vref at 80% */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Mic2 (front panel) pin widget for input and vref at 80% */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + /* Line In pin widget for input */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* Headphone pin widget for output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + /* CD pin widget for input */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + /* unmute amp left and right */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, + /* set connection select to line in (default select for this ADC) */ + {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* unmute Line-Out mixer amp left and right (volume = 0) */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* unmute HP mixer amp left and right (volume = 0) */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* mute pin widget amp left and right (no gain on this amp) */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + /* 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 */ + /* Unmute Front out path */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Headphone out path */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + /* Unmute Mono out path */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + { } +}; + +/* 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_HP] = "hp", + [ALC260_HP_3013] = "hp-3013", + [ALC260_HP_DC7600] = "hp-dc7600", + [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(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */ + SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600), + SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), + SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), + 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_HP] = { + .mixers = { alc260_hp_output_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_setup, + .init_hook = alc_inithook, + }, + [ALC260_HP_DC7600] = { + .mixers = { alc260_hp_dc7600_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_dc7600_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_3012_setup, + .init_hook = alc_inithook, + }, + [ALC260_HP_3013] = { + .mixers = { alc260_hp_3013_mixer, + alc260_input_mixer }, + .init_verbs = { alc260_hp_3013_init_verbs, + alc260_hp_3013_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc260_hp_3013_setup, + .init_hook = alc_inithook, + }, + [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/alc262_quirks.c b/sound/pci/hda/alc262_quirks.c new file mode 100644 index 000000000000..8d2097d77642 --- /dev/null +++ b/sound/pci/hda/alc262_quirks.c @@ -0,0 +1,1353 @@ +/* + * ALC262 quirk models + * included by patch_realtek.c + */ + +/* ALC262 models */ +enum { + ALC262_AUTO, + ALC262_BASIC, + ALC262_HIPPO, + ALC262_HIPPO_1, + ALC262_FUJITSU, + ALC262_HP_BPC, + ALC262_HP_BPC_D7000_WL, + ALC262_HP_BPC_D7000_WF, + ALC262_HP_TC_T5735, + ALC262_HP_RP5700, + ALC262_BENQ_ED8, + ALC262_SONY_ASSAMD, + ALC262_BENQ_T31, + ALC262_ULTRA, + ALC262_LENOVO_3000, + ALC262_NEC, + ALC262_TOSHIBA_S06, + ALC262_TOSHIBA_RX1, + ALC262_TYAN, + ALC262_MODEL_LAST /* last tag */ +}; + +#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID +#define ALC262_DIGIN_NID ALC880_DIGIN_NID + +#define alc262_dac_nids alc260_dac_nids +#define alc262_adc_nids alc882_adc_nids +#define alc262_adc_nids_alt alc882_adc_nids_alt +#define alc262_capsrc_nids alc882_capsrc_nids +#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt + +#define alc262_modes alc260_modes +#define alc262_capture_source alc882_capture_source + +static const hda_nid_t alc262_dmic_adc_nids[1] = { + /* ADC0 */ + 0x09 +}; + +static const hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 }; + +static const struct snd_kcontrol_new alc262_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front 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("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("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +/* update HP, line and mono-out pins according to the master switch */ +#define alc262_hp_master_update alc260_hp_master_update + +static void alc262_hp_bpc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static void alc262_hp_wildwest_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +#define alc262_hp_master_sw_get alc260_hp_master_sw_get +#define alc262_hp_master_sw_put alc260_hp_master_sw_put + +#define ALC262_HP_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hp_master_sw_get, \ + .put = alc262_hp_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \ + } + + +static const struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + ALC262_HP_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + ALC262_HP_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x1a, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { + HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Rear Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hp_t5735_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_hp_t5735_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static const struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_hp_rp5700_verbs[] = { + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {} +}; + +static const struct hda_input_mux alc262_hp_rp5700_capture_source = { + .num_items = 1, + .items = { + { "Line", 0x1 }, + }, +}; + +/* bind hp and internal speaker mute (with plug check) as master switch */ +#define alc262_hippo_master_update alc262_hp_master_update +#define alc262_hippo_master_sw_get alc262_hp_master_sw_get +#define alc262_hippo_master_sw_put alc262_hp_master_sw_put + +#define ALC262_HIPPO_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hippo_master_sw_get, \ + .put = alc262_hippo_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \ + (SUBDEV_SPEAKER(0) << 16), \ + } + +static const struct snd_kcontrol_new alc262_hippo_mixer[] = { + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_hippo1_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hippo_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc262_hippo1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + + +static const struct snd_kcontrol_new alc262_sony_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_benq_t31_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc262_tyan_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_tyan_verbs[] = { + /* Headphone automute */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* P11 AUX_IN, white 4-pin connector */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93}, + {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19}, + + {} +}; + +/* unsolicited event for HP jack sensing */ +static void alc262_tyan_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + + +#define alc262_capture_mixer alc882_capture_mixer +#define alc262_capture_alt_mixer alc882_capture_alt_mixer + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc262_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)}, + + /* Mute 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)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* 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}, + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {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)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, 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, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + + { } +}; + +static const struct hda_verb alc262_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc262_hippo1_unsol_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, + + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_sony_unsol_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_toshiba_s06_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x09}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static void alc262_toshiba_s06_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* + * nec model + * 0x15 = headphone + * 0x16 = internal speaker + * 0x18 = external mic + */ + +static const struct snd_kcontrol_new alc262_nec_mixer[] = { + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_nec_verbs[] = { + /* Unmute Speaker */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Headphone */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* External mic to headphone */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* External mic to speaker */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {} +}; + +/* + * fujitsu model + * 0x14 = headphone/spdif-out, 0x15 = internal speaker, + * 0x1b = port replicator headphone out + */ + +static const struct hda_verb alc262_fujitsu_unsol_verbs[] = { + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc262_lenovo_3000_init_verbs[] = { + /* Front Mic pin: input vref at 50% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {} +}; + +static const struct hda_input_mux alc262_fujitsu_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc262_HP_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "AUX IN", 0x6 }, + }, +}; + +static const struct hda_input_mux alc262_HP_D7000_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x2 }, + { "Line", 0x1 }, + { "CD", 0x4 }, + }, +}; + +static void alc262_fujitsu_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.hp_pins[1] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* bind volumes of both NID 0x0c and 0x0d */ +static const struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc262_fujitsu_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, + { + .iface = NID_MAPPING, + .name = "Master Playback Switch", + .private_value = 0x1b, + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 void alc262_lenovo_3000_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 alc262_toshiba_rx1_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* additional init verbs for Benq laptops */ +static const struct hda_verb alc262_EAPD_verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + {} +}; + +static const struct hda_verb alc262_benq_t31_EAPD_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + {} +}; + +/* Samsung Q1 Ultra Vista model setup */ +static const struct snd_kcontrol_new alc262_ultra_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Mic Boost Volume", 0x15, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc262_ultra_verbs[] = { + /* output mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* speaker */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + /* internal mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* ADC, choose mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, + {} +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_ultra_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + mute = 0; + /* auto-mute only when HP is used as HP */ + if (!spec->cur_mux[0]) { + spec->jack_present = snd_hda_jack_detect(codec, 0x15); + if (spec->jack_present) + mute = HDA_AMP_MUTE; + } + /* mute/unmute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute/unmute HP */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); +} + +/* unsolicited event for HP jack sensing */ +static void alc262_ultra_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_ultra_automute(codec); +} + +static const struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Headphone", 0x7 }, + }, +}; + +static int alc262_ultra_mux_enum_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 ret; + + ret = alc_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); + alc262_ultra_automute(codec); /* mute/unmute HP */ + return ret; +} + +static const struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { + .iface = NID_MAPPING, + .name = "Capture Source", + .private_value = 0x15, + }, + { } /* end */ +}; + +static const struct hda_verb alc262_HP_BPC_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)}, + + /* Mute 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(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* 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}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {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)}, + + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */ + /* Input mixer1: only unmute Mic */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, + + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } +}; + +static const struct hda_verb alc262_HP_BPC_WildWest_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)}, + + /* Mute 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(5)}, + {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 - 0x0e) + */ + /* 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}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {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)}, + + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + /* 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, (0x7000 | (0x00 << 8))}, /*rear MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ + /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } +}; + +static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* + * configuration and preset + */ +static const char * const alc262_models[ALC262_MODEL_LAST] = { + [ALC262_BASIC] = "basic", + [ALC262_HIPPO] = "hippo", + [ALC262_HIPPO_1] = "hippo_1", + [ALC262_FUJITSU] = "fujitsu", + [ALC262_HP_BPC] = "hp-bpc", + [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", + [ALC262_HP_TC_T5735] = "hp-tc-t5735", + [ALC262_HP_RP5700] = "hp-rp5700", + [ALC262_BENQ_ED8] = "benq", + [ALC262_BENQ_T31] = "benq-t31", + [ALC262_SONY_ASSAMD] = "sony-assamd", + [ALC262_TOSHIBA_S06] = "toshiba-s06", + [ALC262_TOSHIBA_RX1] = "toshiba-rx1", + [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", + [ALC262_NEC] = "nec", + [ALC262_TYAN] = "tyan", + [ALC262_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc262_cfg_tbl[] = { + SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), + SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series", + ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", + ALC262_AUTO), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", + ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", + ALC262_HP_TC_T5735), + SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), + SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */ + SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), + SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), +#if 0 /* disable the quirk since model=auto works better in recent versions */ + SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", + ALC262_SONY_ASSAMD), +#endif + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", + ALC262_TOSHIBA_RX1), + SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), + SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), + SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN), + SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1", + ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), + SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), + SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), + {} +}; + +static const struct alc_config_preset alc262_presets[] = { + [ALC262_BASIC] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_HIPPO] = { + .mixers = { alc262_hippo_mixer }, + .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_HIPPO_1] = { + .mixers = { alc262_hippo1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo1_setup, + .init_hook = alc_inithook, + }, + [ALC262_FUJITSU] = { + .mixers = { alc262_fujitsu_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_fujitsu_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_fujitsu_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC] = { + .mixers = { alc262_HP_BPC_mixer }, + .init_verbs = { alc262_HP_BPC_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_bpc_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC_D7000_WF] = { + .mixers = { alc262_HP_BPC_WildWest_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_wildwest_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_BPC_D7000_WL] = { + .mixers = { alc262_HP_BPC_WildWest_mixer, + alc262_HP_BPC_WildWest_option_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_wildwest_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_TC_T5735] = { + .mixers = { alc262_hp_t5735_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hp_t5735_setup, + .init_hook = alc_inithook, + }, + [ALC262_HP_RP5700] = { + .mixers = { alc262_hp_rp5700_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_hp_rp5700_capture_source, + }, + [ALC262_BENQ_ED8] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_SONY_ASSAMD] = { + .mixers = { alc262_sony_mixer }, + .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_BENQ_T31] = { + .mixers = { alc262_benq_t31_mixer }, + .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, + alc_hp15_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_ULTRA] = { + .mixers = { alc262_ultra_mixer }, + .cap_mixer = alc262_ultra_capture_mixer, + .init_verbs = { alc262_ultra_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_ultra_capture_source, + .adc_nids = alc262_adc_nids, /* ADC0 */ + .capsrc_nids = alc262_capsrc_nids, + .num_adc_nids = 1, /* single ADC */ + .unsol_event = alc262_ultra_unsol_event, + .init_hook = alc262_ultra_automute, + }, + [ALC262_LENOVO_3000] = { + .mixers = { alc262_lenovo_3000_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_lenovo_3000_unsol_verbs, + alc262_lenovo_3000_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_lenovo_3000_setup, + .init_hook = alc_inithook, + }, + [ALC262_NEC] = { + .mixers = { alc262_nec_mixer }, + .init_verbs = { alc262_nec_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_TOSHIBA_S06] = { + .mixers = { alc262_toshiba_s06_mixer }, + .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, + alc262_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .capsrc_nids = alc262_dmic_capsrc_nids, + .dac_nids = alc262_dac_nids, + .adc_nids = alc262_dmic_adc_nids, /* ADC0 */ + .num_adc_nids = 1, /* single ADC */ + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_toshiba_s06_setup, + .init_hook = alc_inithook, + }, + [ALC262_TOSHIBA_RX1] = { + .mixers = { alc262_toshiba_rx1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_hippo_setup, + .init_hook = alc_inithook, + }, + [ALC262_TYAN] = { + .mixers = { alc262_tyan_mixer }, + .init_verbs = { alc262_init_verbs, alc262_tyan_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_tyan_setup, + .init_hook = alc_hp_automute, + }, +}; + diff --git a/sound/pci/hda/alc268_quirks.c b/sound/pci/hda/alc268_quirks.c new file mode 100644 index 000000000000..be58bf2f3aec --- /dev/null +++ b/sound/pci/hda/alc268_quirks.c @@ -0,0 +1,636 @@ +/* + * ALC267/ALC268 quirk models + * included by patch_realtek.c + */ + +/* ALC268 models */ +enum { + ALC268_AUTO, + ALC267_QUANTA_IL1, + ALC268_3ST, + ALC268_TOSHIBA, + ALC268_ACER, + ALC268_ACER_DMIC, + ALC268_ACER_ASPIRE_ONE, + ALC268_DELL, + ALC268_ZEPTO, +#ifdef CONFIG_SND_DEBUG + ALC268_TEST, +#endif + ALC268_MODEL_LAST /* last tag */ +}; + +/* + * ALC268 channel source setting (2 channel) + */ +#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID +#define alc268_modes alc260_modes + +static const hda_nid_t alc268_dac_nids[2] = { + /* front, hp */ + 0x02, 0x03 +}; + +static const hda_nid_t alc268_adc_nids[2] = { + /* ADC0-1 */ + 0x08, 0x07 +}; + +static const hda_nid_t alc268_adc_nids_alt[1] = { + /* ADC0 */ + 0x08 +}; + +static const hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; + +static const struct snd_kcontrol_new alc268_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* Toshiba specific */ +static const struct hda_verb alc268_toshiba_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* Acer specific */ +/* bind volumes of both NID 0x02 and 0x03 */ +static const struct hda_bind_ctls alc268_acer_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static void alc268_acer_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#define alc268_acer_master_sw_get alc262_hp_master_sw_get +#define alc268_acer_master_sw_put alc262_hp_master_sw_put + +static const struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x15, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_acer_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .info = snd_ctl_boolean_mono_info, + .get = alc268_acer_master_sw_get, + .put = alc268_acer_master_sw_put, + }, + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_acer_aspire_one_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x06}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017}, + { } +}; + +static const struct hda_verb alc268_acer_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +/* unsolicited event for HP jack sensing */ +#define alc268_toshiba_setup alc262_hippo_setup + +static void alc268_acer_lc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static const struct snd_kcontrol_new alc268_dell_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc268_dell_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc268_dell_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +static const struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_verb alc267_quanta_il1_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc267_quanta_il1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc268_base_init_verbs[] = { + /* Unmute DAC0-1 and set vol = 0 */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* Unmute Selector 23h,24h and set the default input to mic-in */ + + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + { } +}; + +/* only for model=test */ +#ifdef CONFIG_SND_DEBUG +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc268_volume_init_verbs[] = { + /* set output DAC */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; +#endif /* CONFIG_SND_DEBUG */ + +static const struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc268_capture_alt_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + _DEFINE_CAPSRC(1), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc268_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT), + _DEFINE_CAPSRC(2), + { } /* end */ +}; + +static const struct hda_input_mux alc268_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x3 }, + }, +}; + +static const struct hda_input_mux alc268_acer_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc268_acer_dmic_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x6 }, + { "Line", 0x2 }, + }, +}; + +#ifdef CONFIG_SND_DEBUG +static const struct snd_kcontrol_new alc268_test_mixer[] = { + /* Volume widgets */ + HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), + HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), + HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), + HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), + /* The below appears problematic on some hardwares */ + /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ + HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), + + /* 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 SPDIF output pin to be enabled. + * The ALC268 does not have an SPDIF input. + */ + ALC_SPDIF_CTRL_SWITCH("SPDIF Playback 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 */ +}; +#endif + +/* + * configuration and preset + */ +static const char * const alc268_models[ALC268_MODEL_LAST] = { + [ALC267_QUANTA_IL1] = "quanta-il1", + [ALC268_3ST] = "3stack", + [ALC268_TOSHIBA] = "toshiba", + [ALC268_ACER] = "acer", + [ALC268_ACER_DMIC] = "acer-dmic", + [ALC268_ACER_ASPIRE_ONE] = "acer-aspire", + [ALC268_DELL] = "dell", + [ALC268_ZEPTO] = "zepto", +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = "test", +#endif + [ALC268_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One", + ALC268_ACER_ASPIRE_ONE), + SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), + SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron 910", ALC268_AUTO), + SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0, + "Dell Inspiron Mini9/Vostro A90", ALC268_DELL), + /* almost compatible with toshiba but with optional digital outs; + * auto-probing seems working fine + */ + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", + ALC268_AUTO), + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), + SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), + {} +}; + +/* Toshiba laptops have no unique PCI SSID but only codec SSID */ +static const struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { + SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO), + SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO), + SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05", + ALC268_TOSHIBA), + {} +}; + +static const struct alc_config_preset alc268_presets[] = { + [ALC267_QUANTA_IL1] = { + .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, + alc268_capture_nosrc_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc267_quanta_il1_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc267_quanta_il1_setup, + .init_hook = alc_inithook, + }, + [ALC268_3ST] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + }, + [ALC268_TOSHIBA] = { + .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_toshiba_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER] = { + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_acer_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER_DMIC] = { + .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_acer_dmic_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_setup, + .init_hook = alc_inithook, + }, + [ALC268_ACER_ASPIRE_ONE] = { + .mixers = { alc268_acer_aspire_one_mixer, + alc268_beep_mixer, + alc268_capture_nosrc_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_aspire_one_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_acer_lc_setup, + .init_hook = alc_inithook, + }, + [ALC268_DELL] = { + .mixers = { alc268_dell_mixer, alc268_beep_mixer, + alc268_capture_nosrc_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_dell_setup, + .init_hook = alc_inithook, + }, + [ALC268_ZEPTO] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc268_toshiba_setup, + .init_hook = alc_inithook, + }, +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = { + .mixers = { alc268_test_mixer, alc268_capture_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_volume_init_verbs, + alc268_beep_init_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + }, +#endif +}; + diff --git a/sound/pci/hda/alc269_quirks.c b/sound/pci/hda/alc269_quirks.c new file mode 100644 index 000000000000..14fdcf29b154 --- /dev/null +++ b/sound/pci/hda/alc269_quirks.c @@ -0,0 +1,681 @@ +/* + * ALC269/ALC270/ALC275/ALC276 quirk models + * included by patch_realtek.c + */ + +/* ALC269 models */ +enum { + ALC269_AUTO, + ALC269_BASIC, + ALC269_QUANTA_FL1, + ALC269_AMIC, + ALC269_DMIC, + ALC269VB_AMIC, + ALC269VB_DMIC, + ALC269_FUJITSU, + ALC269_LIFEBOOK, + ALC271_ACER, + ALC269_MODEL_LAST /* last tag */ +}; + +/* + * ALC269 channel source setting (2 channel) + */ +#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID + +#define alc269_dac_nids alc260_dac_nids + +static const hda_nid_t alc269_adc_nids[1] = { + /* ADC1 */ + 0x08, +}; + +static const hda_nid_t alc269_capsrc_nids[1] = { + 0x23, +}; + +static const hda_nid_t alc269vb_adc_nids[1] = { + /* ADC1 */ + 0x09, +}; + +static const hda_nid_t alc269vb_capsrc_nids[1] = { + 0x22, +}; + +#define alc269_modes alc260_modes +#define alc269_capture_source alc880_lg_lw_capture_source + +static const struct snd_kcontrol_new alc269_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc269_lifebook_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x1b, 0, HDA_INPUT), + { } +}; + +static const struct snd_kcontrol_new alc269_laptop_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269_asus_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* capture mixer elements */ +static const struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +/* FSC amilo */ +#define alc269_fujitsu_mixer alc269_laptop_mixer + +static const struct hda_verb alc269_quanta_fl1_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; + +static const struct hda_verb alc269_lifebook_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) +{ + alc_hp_automute(codec); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x680); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x480); +} + +#define alc269_lifebook_speaker_automute \ + alc269_quanta_fl1_speaker_automute + +static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) +{ + unsigned int present_laptop; + unsigned int present_dock; + + present_laptop = snd_hda_jack_detect(codec, 0x18); + present_dock = snd_hda_jack_detect(codec, 0x1b); + + /* Laptop mic port overrides dock mic port, design decision */ + if (present_dock) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x3); + if (present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x0); + if (!present_dock && !present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x1); +} + +static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_HP_EVENT: + alc269_quanta_fl1_speaker_automute(codec); + break; + case ALC_MIC_EVENT: + alc_mic_automute(codec); + break; + } +} + +static void alc269_lifebook_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc269_lifebook_speaker_automute(codec); + if ((res >> 26) == ALC_MIC_EVENT) + alc269_lifebook_mic_autoswitch(codec); +} + +static void alc269_quanta_fl1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) +{ + alc269_quanta_fl1_speaker_automute(codec); + alc_mic_automute(codec); +} + +static void alc269_lifebook_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.hp_pins[1] = 0x1a; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; +} + +static void alc269_lifebook_init_hook(struct hda_codec *codec) +{ + alc269_lifebook_speaker_automute(codec); + alc269_lifebook_mic_autoswitch(codec); +} + +static const struct hda_verb alc269_laptop_dmic_init_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269_laptop_amic_init_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269vb_laptop_dmic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x06}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc269vb_laptop_amic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc271_acer_dmic_verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x22, AC_VERB_SET_CONNECT_SEL, 6}, + { } +}; + +static void alc269_laptop_amic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static void alc269vb_laptop_amic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc269_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* + * Set up output mixers (0x02 - 0x03) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, 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_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)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {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}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* FIXME: use Mux-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc269vb_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* + * Set up output mixers (0x02 - 0x03) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, 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_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)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {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}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* FIXME: use Mux-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * configuration and preset + */ +static const char * const alc269_models[ALC269_MODEL_LAST] = { + [ALC269_BASIC] = "basic", + [ALC269_QUANTA_FL1] = "quanta", + [ALC269_AMIC] = "laptop-amic", + [ALC269_DMIC] = "laptop-dmic", + [ALC269_FUJITSU] = "fujitsu", + [ALC269_LIFEBOOK] = "lifebook", + [ALC269_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc269_cfg_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), + SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER), + SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", + ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), + SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), + SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), + SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), + SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC), + SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC), + SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC), + {} +}; + +static const struct alc_config_preset alc269_presets[] = { + [ALC269_BASIC] = { + .mixers = { alc269_base_mixer }, + .init_verbs = { alc269_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + }, + [ALC269_QUANTA_FL1] = { + .mixers = { alc269_quanta_fl1_mixer }, + .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .unsol_event = alc269_quanta_fl1_unsol_event, + .setup = alc269_quanta_fl1_setup, + .init_hook = alc269_quanta_fl1_init_hook, + }, + [ALC269_AMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_analog_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc_inithook, + }, + [ALC269_DMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269VB_AMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_analog_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_amic_setup, + .init_hook = alc_inithook, + }, + [ALC269VB_DMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_digital_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269_FUJITSU] = { + .mixers = { alc269_fujitsu_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc_inithook, + }, + [ALC269_LIFEBOOK] = { + .mixers = { alc269_lifebook_mixer }, + .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .unsol_event = alc269_lifebook_unsol_event, + .setup = alc269_lifebook_setup, + .init_hook = alc269_lifebook_init_hook, + }, + [ALC271_ACER] = { + .mixers = { alc269_asus_mixer }, + .cap_mixer = alc269vb_laptop_digital_capture_mixer, + .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .adc_nids = alc262_dmic_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids), + .capsrc_nids = alc262_dmic_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + .dig_out_nid = ALC880_DIGOUT_NID, + .unsol_event = alc_sku_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc_inithook, + }, +}; + diff --git a/sound/pci/hda/alc662_quirks.c b/sound/pci/hda/alc662_quirks.c new file mode 100644 index 000000000000..e69a6ea3083a --- /dev/null +++ b/sound/pci/hda/alc662_quirks.c @@ -0,0 +1,1408 @@ +/* + * ALC662/ALC663/ALC665/ALC670 quirk models + * included by patch_realtek.c + */ + +/* ALC662 models */ +enum { + ALC662_AUTO, + ALC662_3ST_2ch_DIG, + ALC662_3ST_6ch_DIG, + ALC662_3ST_6ch, + ALC662_5ST_DIG, + ALC662_LENOVO_101E, + ALC662_ASUS_EEEPC_P701, + ALC662_ASUS_EEEPC_EP20, + ALC663_ASUS_M51VA, + ALC663_ASUS_G71V, + ALC663_ASUS_H13, + ALC663_ASUS_G50V, + ALC662_ECS, + ALC663_ASUS_MODE1, + ALC662_ASUS_MODE2, + ALC663_ASUS_MODE3, + ALC663_ASUS_MODE4, + ALC663_ASUS_MODE5, + ALC663_ASUS_MODE6, + ALC663_ASUS_MODE7, + ALC663_ASUS_MODE8, + ALC272_DELL, + ALC272_DELL_ZM1, + ALC272_SAMSUNG_NC10, + ALC662_MODEL_LAST, +}; + +#define ALC662_DIGOUT_NID 0x06 +#define ALC662_DIGIN_NID 0x0a + +static const hda_nid_t alc662_dac_nids[3] = { + /* front, rear, clfe */ + 0x02, 0x03, 0x04 +}; + +static const hda_nid_t alc272_dac_nids[2] = { + 0x02, 0x03 +}; + +static const hda_nid_t alc662_adc_nids[2] = { + /* ADC1-2 */ + 0x09, 0x08 +}; + +static const hda_nid_t alc272_adc_nids[1] = { + /* ADC1-2 */ + 0x08, +}; + +static const hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 }; +static const hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; + + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ +static const struct hda_input_mux alc662_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc662_lenovo_101e_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc663_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +#if 0 /* set to 1 for testing other input sources below */ +static const struct hda_input_mux alc272_nc10_capture_source = { + .num_items = 16, + .items = { + { "Autoselect Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "In-0x02", 0x2 }, + { "In-0x03", 0x3 }, + { "In-0x04", 0x4 }, + { "In-0x05", 0x5 }, + { "In-0x06", 0x6 }, + { "In-0x07", 0x7 }, + { "In-0x08", 0x8 }, + { "In-0x09", 0x9 }, + { "In-0x0a", 0x0a }, + { "In-0x0b", 0x0b }, + { "In-0x0c", 0x0c }, + { "In-0x0d", 0x0d }, + { "In-0x0e", 0x0e }, + { "In-0x0f", 0x0f }, + }, +}; +#endif + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc662_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc662_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, 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 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc662_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc662_3ST_6ch_modes[2] = { + { 2, alc662_3ST_ch2_init }, + { 6, alc662_3ST_ch6_init }, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc662_sixstack_ch6_init[] = { + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc662_sixstack_ch8_init[] = { + { 0x16, 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc662_5stack_modes[2] = { + { 2, alc662_sixstack_ch6_init }, + { 6, alc662_sixstack_ch8_init }, +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ + +static const struct snd_kcontrol_new alc662_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + /*Input mixer control */ + HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, 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_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), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, 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_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), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, 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, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 alc662_eeepc_ep20_mixer[] = { + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, 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), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_one_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_m51va_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_tree_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_four_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc662_1bjd_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_two_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_two_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", + &alc663_asus_two_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc663_g71v_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + 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 alc663_g50v_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + 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), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc663_mode7_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, 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 snd_kcontrol_new alc663_mode8_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + +static const struct snd_kcontrol_new alc662_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 alc662_init_verbs[] = { + /* ADC: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {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)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* 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 mixer */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + { } +}; + +static const struct hda_verb alc662_eapd_init_verbs[] = { + /* always trun on EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc662_sue_init_verbs[] = { + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_FRONT_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_eeepc_sue_init_verbs[] = { + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +/* Set Unsolicited Event*/ +static const struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_m51va_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_21jd_amic_init_verbs[] = { + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_1bjd_amic_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {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}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_15jd_amic_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {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, 0x01}, /* Headphone */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_g71v_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ + /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */ + + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_FRONT_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_g50v_init_verbs[] = { + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ + + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc662_ecs_init_verbs[] = { + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc272_dell_zm1_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc272_dell_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_mode7_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, 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_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct hda_verb alc663_mode8_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {} +}; + +static const struct snd_kcontrol_new alc662_auto_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc272_auto_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +static void alc662_lenovo_101e_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc662_eeepc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc262_hippo1_setup(codec); + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +static void alc662_eeepc_ep20_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc663_m51va_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +/* ***************** Mode1 ******************************/ +static void alc663_mode1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode2 ******************************/ +static void alc662_mode2_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode3 ******************************/ +static void alc663_mode3_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode4 ******************************/ +static void alc663_mode4_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute_mixer_nid[1] = 0x0e; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode5 ******************************/ +static void alc663_mode5_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute_mixer_nid[1] = 0x0e; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode6 ******************************/ +static void alc663_mode6_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute_mixer_nid[0] = 0x0c; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_MIXER; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode7 ******************************/ +static void alc663_mode7_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; +} + +/* ***************** Mode8 ******************************/ +static void alc663_mode8_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.hp_pins[1] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_PIN; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +static void alc663_g71v_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.line_out_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x12; + spec->auto_mic = 1; +} + +#define alc663_g50v_setup alc663_m51va_setup + +static const struct snd_kcontrol_new alc662_ecs_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + + HDA_CODEC_VOLUME("Mic/LineIn Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT), + + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, 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 alc272_nc10_mixer[] = { + /* Master Playback automatically created from Speaker and Headphone */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + + +/* + * configuration and preset + */ +static const char * const alc662_models[ALC662_MODEL_LAST] = { + [ALC662_3ST_2ch_DIG] = "3stack-dig", + [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", + [ALC662_3ST_6ch] = "3stack-6ch", + [ALC662_5ST_DIG] = "5stack-dig", + [ALC662_LENOVO_101E] = "lenovo-101e", + [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", + [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", + [ALC662_ECS] = "ecs", + [ALC663_ASUS_M51VA] = "m51va", + [ALC663_ASUS_G71V] = "g71v", + [ALC663_ASUS_H13] = "h13", + [ALC663_ASUS_G50V] = "g50v", + [ALC663_ASUS_MODE1] = "asus-mode1", + [ALC662_ASUS_MODE2] = "asus-mode2", + [ALC663_ASUS_MODE3] = "asus-mode3", + [ALC663_ASUS_MODE4] = "asus-mode4", + [ALC663_ASUS_MODE5] = "asus-mode5", + [ALC663_ASUS_MODE6] = "asus-mode6", + [ALC663_ASUS_MODE7] = "asus-mode7", + [ALC663_ASUS_MODE8] = "asus-mode8", + [ALC272_DELL] = "dell", + [ALC272_DELL_ZM1] = "dell-zm1", + [ALC272_SAMSUNG_NC10] = "samsung-nc10", + [ALC662_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc662_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), + SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL), + SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), + SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), + SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), + /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/ + SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), + /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/ + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), + SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), + SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), + SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), + SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), + SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), + SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), + SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", + ALC663_ASUS_H13), + SND_PCI_QUIRK(0x1991, 0x5628, "Ordissimo EVE", ALC662_LENOVO_101E), + {} +}; + +static const struct alc_config_preset alc662_presets[] = { + [ALC662_3ST_2ch_DIG] = { + .mixers = { alc662_3ST_2ch_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch_DIG] = { + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch] = { + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc662_capture_source, + }, + [ALC662_5ST_DIG] = { + .mixers = { alc662_base_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .dig_in_nid = ALC662_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), + .channel_mode = alc662_5stack_modes, + .input_mux = &alc662_capture_source, + }, + [ALC662_LENOVO_101E] = { + .mixers = { alc662_lenovo_101e_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_lenovo_101e_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_EEEPC_P701] = { + .mixers = { alc662_eeepc_p701_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_eeepc_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_EEEPC_EP20] = { + .mixers = { alc662_eeepc_ep20_mixer, + alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_eeepc_ep20_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_ep20_setup, + .init_hook = alc_inithook, + }, + [ALC662_ECS] = { + .mixers = { alc662_ecs_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_ecs_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_eeepc_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_M51VA] = { + .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_G71V] = { + .mixers = { alc663_g71v_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_g71v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_g71v_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_H13] = { + .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .setup = alc663_m51va_setup, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_G50V] = { + .mixers = { alc663_g50v_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_g50v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc663_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_g50v_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE1] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode1_setup, + .init_hook = alc_inithook, + }, + [ALC662_ASUS_MODE2] = { + .mixers = { alc662_1bjd_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc662_1bjd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc662_mode2_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE3] = { + .mixers = { alc663_two_hp_m1_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_two_hp_amic_m1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode3_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE4] = { + .mixers = { alc663_asus_21jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs}, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode4_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE5] = { + .mixers = { alc663_asus_15jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_15jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode5_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE6] = { + .mixers = { alc663_two_hp_m2_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_two_hp_amic_m2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode6_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE7] = { + .mixers = { alc663_mode7_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_mode7_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode7_setup, + .init_hook = alc_inithook, + }, + [ALC663_ASUS_MODE8] = { + .mixers = { alc663_mode8_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_mode8_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode8_setup, + .init_hook = alc_inithook, + }, + [ALC272_DELL] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc272_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc272_dell_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .adc_nids = alc272_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), + .capsrc_nids = alc272_capsrc_nids, + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC272_DELL_ZM1] = { + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc272_dell_zm1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .adc_nids = alc662_adc_nids, + .num_adc_nids = 1, + .capsrc_nids = alc662_capsrc_nids, + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc_sku_unsol_event, + .setup = alc663_m51va_setup, + .init_hook = alc_inithook, + }, + [ALC272_SAMSUNG_NC10] = { + .mixers = { alc272_nc10_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eapd_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + /*.input_mux = &alc272_nc10_capture_source,*/ + .unsol_event = alc_sku_unsol_event, + .setup = alc663_mode4_setup, + .init_hook = alc_inithook, + }, +}; + + diff --git a/sound/pci/hda/alc680_quirks.c b/sound/pci/hda/alc680_quirks.c new file mode 100644 index 000000000000..0eeb227c7bc2 --- /dev/null +++ b/sound/pci/hda/alc680_quirks.c @@ -0,0 +1,222 @@ +/* + * ALC680 quirk models + * included by patch_realtek.c + */ + +/* ALC680 models */ +enum { + ALC680_AUTO, + ALC680_BASE, + ALC680_MODEL_LAST, +}; + +#define ALC680_DIGIN_NID ALC880_DIGIN_NID +#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID +#define alc680_modes alc260_modes + +static const hda_nid_t alc680_dac_nids[3] = { + /* Lout1, Lout2, hp */ + 0x02, 0x03, 0x04 +}; + +static const hda_nid_t alc680_adc_nids[3] = { + /* ADC0-2 */ + /* DMIC, MIC, Line-in*/ + 0x07, 0x08, 0x09 +}; + +/* + * Analog capture ADC cgange + */ +static hda_nid_t alc680_get_cur_adc(struct hda_codec *codec) +{ + static hda_nid_t pins[] = {0x18, 0x19}; + static hda_nid_t adcs[] = {0x08, 0x09}; + int i; + + for (i = 0; i < ARRAY_SIZE(pins); i++) { + if (!is_jack_detectable(codec, pins[i])) + continue; + if (snd_hda_jack_detect(codec, pins[i])) + return adcs[i]; + } + return 0x07; +} + +static void alc680_rec_autoswitch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = alc680_get_cur_adc(codec); + if (spec->cur_adc && nid != spec->cur_adc) { + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = nid; + snd_hda_codec_setup_stream(codec, nid, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + +static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = alc680_get_cur_adc(codec); + + spec->cur_adc = nid; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); + return 0; +} + +static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +static const struct hda_pcm_stream alc680_pcm_analog_auto_capture = { + .substreams = 1, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .prepare = alc680_capture_pcm_prepare, + .cleanup = alc680_capture_pcm_cleanup + }, +}; + +static const struct snd_kcontrol_new alc680_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x12, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost Volume", 0x19, 0, HDA_INPUT), + { } +}; + +static const struct hda_bind_ctls alc680_bind_cap_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc680_bind_cap_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc680_master_capture_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch), + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc680_init_verbs[] = { + {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}, + + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc680_base_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x16; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.num_inputs = 2; + spec->autocfg.inputs[0].pin = 0x18; + spec->autocfg.inputs[0].type = AUTO_PIN_MIC; + spec->autocfg.inputs[1].pin = 0x19; + spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc680_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc_hp_automute(codec); + if ((res >> 26) == ALC_MIC_EVENT) + alc680_rec_autoswitch(codec); +} + +static void alc680_inithook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc680_rec_autoswitch(codec); +} + +/* + * configuration and preset + */ +static const char * const alc680_models[ALC680_MODEL_LAST] = { + [ALC680_BASE] = "base", + [ALC680_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc680_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE), + {} +}; + +static const struct alc_config_preset alc680_presets[] = { + [ALC680_BASE] = { + .mixers = { alc680_base_mixer }, + .cap_mixer = alc680_master_capture_mixer, + .init_verbs = { alc680_init_verbs }, + .num_dacs = ARRAY_SIZE(alc680_dac_nids), + .dac_nids = alc680_dac_nids, + .dig_out_nid = ALC680_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc680_modes), + .channel_mode = alc680_modes, + .unsol_event = alc680_unsol_event, + .setup = alc680_base_setup, + .init_hook = alc680_inithook, + + }, +}; diff --git a/sound/pci/hda/alc861_quirks.c b/sound/pci/hda/alc861_quirks.c new file mode 100644 index 000000000000..d719ec6350eb --- /dev/null +++ b/sound/pci/hda/alc861_quirks.c @@ -0,0 +1,725 @@ +/* + * ALC660/ALC861 quirk models + * included by patch_realtek.c + */ + +/* ALC861 models */ +enum { + ALC861_AUTO, + ALC861_3ST, + ALC660_3ST, + ALC861_3ST_DIG, + ALC861_6ST_DIG, + ALC861_UNIWILL_M31, + ALC861_TOSHIBA, + ALC861_ASUS, + ALC861_ASUS_LAPTOP, + ALC861_MODEL_LAST, +}; + +/* + * ALC861 channel source setting (2/6 channel selection for 3-stack) + */ + +/* + * set the path ways for 2 channel output + * need to set the codec line out and mic 1 pin widgets to inputs + */ +static const struct hda_verb alc861_threestack_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1/2) for input, for mic also enable + * the vref + */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif + { } /* end */ +}; +/* + * 6ch mode + * need to set the codec line out and mic 1 pin widgets to outputs + */ +static const struct hda_verb alc861_threestack_ch6_init[] = { + /* set pin widget 1Ah (line in) for output (Back Surround)*/ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* set pin widget 18h (mic1) for output (CLFE)*/ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + + { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif + { } /* end */ +}; + +static const struct hda_channel_mode alc861_threestack_modes[2] = { + { 2, alc861_threestack_ch2_init }, + { 6, alc861_threestack_ch6_init }, +}; +/* Set mic1 as input and unmute the mixer */ +static const struct hda_verb alc861_uniwill_m31_ch2_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; +/* Set mic1 as output and mute mixer */ +static const struct hda_verb alc861_uniwill_m31_ch4_init[] = { + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { } /* end */ +}; + +static const struct hda_channel_mode alc861_uniwill_m31_modes[2] = { + { 2, alc861_uniwill_m31_ch2_init }, + { 4, alc861_uniwill_m31_ch4_init }, +}; + +/* Set mic1 and line-in as input and unmute the mixer */ +static const struct hda_verb alc861_asus_ch2_init[] = { + /* set pin widget 1Ah (line in) for input */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* set pin widget 18h (mic1/2) for input, for mic also enable + * the vref + */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif + { } /* end */ +}; +/* Set mic1 nad line-in as output and mute mixer */ +static const struct hda_verb alc861_asus_ch6_init[] = { + /* set pin widget 1Ah (line in) for output (Back Surround)*/ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + /* set pin widget 18h (mic1) for output (CLFE)*/ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ + { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif + { } /* end */ +}; + +static const struct hda_channel_mode alc861_asus_modes[2] = { + { 2, alc861_asus_ch2_init }, + { 6, alc861_asus_ch6_init }, +}; + +/* patch-ALC861 */ + +static const struct snd_kcontrol_new alc861_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), + + /*Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_3ST_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ + + /* Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, + .private_value = ARRAY_SIZE(alc861_threestack_modes), + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ + + /* Input mixer control */ + /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, + .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), + }, + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861_asus_mixer[] = { + /* output mixer control */ + HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), + + /* Input mixer control */ + HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, + .private_value = ARRAY_SIZE(alc861_asus_modes), + }, + { } +}; + +/* additional mixer */ +static const struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), + { } +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc861_base_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute DAC0~3 & spdif out*/ + {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}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + + { } +}; + +static const struct hda_verb alc861_threestack_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {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}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +static const struct hda_verb alc861_uniwill_m31_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + /* this has to be set to VREF80 */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {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}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +static const struct hda_verb alc861_asus_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + /* port-A for surround (rear panel) + * according to codec#0 this is the HP jack + */ + { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ + /* route front PCM to HP */ + { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, + /* port-B for mic-in (rear panel) with vref */ + { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-C for line-in (rear panel) */ + { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* port-D for Front */ + { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-E for HP out (front panel) */ + /* this has to be set to VREF80 */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* route front PCM to HP */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + /* port-F for mic-in (front panel) with vref */ + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + /* port-G for CLFE (rear panel) */ + { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* port-H for side (rear panel) */ + { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, + /* CD-in */ + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, + /* route front mic to ADC1*/ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Unmute DAC0~3 & spdif out*/ + {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}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Unmute Mixer 14 (mic) 1c (Line in)*/ + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Unmute Stereo Mixer 15 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ + + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* hp used DAC 3 (Front) */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + { } +}; + +/* additional init verbs for ASUS laptops */ +static const struct hda_verb alc861_asus_laptop_init_verbs[] = { + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ + { } +}; + +static const struct hda_verb alc861_toshiba_init_verbs[] = { + {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc861_toshiba_automute(struct hda_codec *codec) +{ + unsigned int present = snd_hda_jack_detect(codec, 0x0f); + + snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, + HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); +} + +static void alc861_toshiba_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc861_toshiba_automute(codec); +} + +#define ALC861_DIGOUT_NID 0x07 + +static const struct hda_channel_mode alc861_8ch_modes[1] = { + { 8, NULL } +}; + +static const hda_nid_t alc861_dac_nids[4] = { + /* front, surround, clfe, side */ + 0x03, 0x06, 0x05, 0x04 +}; + +static const hda_nid_t alc660_dac_nids[3] = { + /* front, clfe, surround */ + 0x03, 0x05, 0x06 +}; + +static const hda_nid_t alc861_adc_nids[1] = { + /* ADC0-2 */ + 0x08, +}; + +static const struct hda_input_mux alc861_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x1 }, + { "CD", 0x4 }, + { "Mixer", 0x5 }, + }, +}; + +/* + * configuration and preset + */ +static const char * const alc861_models[ALC861_MODEL_LAST] = { + [ALC861_3ST] = "3stack", + [ALC660_3ST] = "3stack-660", + [ALC861_3ST_DIG] = "3stack-dig", + [ALC861_6ST_DIG] = "6stack-dig", + [ALC861_UNIWILL_M31] = "uniwill-m31", + [ALC861_TOSHIBA] = "toshiba", + [ALC861_ASUS] = "asus", + [ALC861_ASUS_LAPTOP] = "asus-laptop", + [ALC861_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc861_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), + SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), + /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) + * Any other models that need this preset? + */ + /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ + SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), + SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), + /* FIXME: the below seems conflict */ + /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ + SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), + SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), + {} +}; + +static const struct alc_config_preset alc861_presets[] = { + [ALC861_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_3ST_DIG] = { + .mixers = { alc861_base_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_6ST_DIG] = { + .mixers = { alc861_base_mixer }, + .init_verbs = { alc861_base_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes), + .channel_mode = alc861_8ch_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC660_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660_dac_nids), + .dac_nids = alc660_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_UNIWILL_M31] = { + .mixers = { alc861_uniwill_m31_mixer }, + .init_verbs = { alc861_uniwill_m31_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), + .channel_mode = alc861_uniwill_m31_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_TOSHIBA] = { + .mixers = { alc861_toshiba_mixer }, + .init_verbs = { alc861_base_init_verbs, + alc861_toshiba_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + .unsol_event = alc861_toshiba_unsol_event, + .init_hook = alc861_toshiba_automute, + }, + [ALC861_ASUS] = { + .mixers = { alc861_asus_mixer }, + .init_verbs = { alc861_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), + .channel_mode = alc861_asus_modes, + .need_dac_fix = 1, + .hp_nid = 0x06, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, + [ALC861_ASUS_LAPTOP] = { + .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, + .init_verbs = { alc861_asus_init_verbs, + alc861_asus_laptop_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861_dac_nids), + .dac_nids = alc861_dac_nids, + .dig_out_nid = ALC861_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, +}; + diff --git a/sound/pci/hda/alc861vd_quirks.c b/sound/pci/hda/alc861vd_quirks.c new file mode 100644 index 000000000000..8f28450f41f8 --- /dev/null +++ b/sound/pci/hda/alc861vd_quirks.c @@ -0,0 +1,605 @@ +/* + * ALC660-VD/ALC861-VD quirk models + * included by patch_realtek.c + */ + +/* ALC861-VD models */ +enum { + ALC861VD_AUTO, + ALC660VD_3ST, + ALC660VD_3ST_DIG, + ALC660VD_ASUS_V1S, + ALC861VD_3ST, + ALC861VD_3ST_DIG, + ALC861VD_6ST_DIG, + ALC861VD_LENOVO, + ALC861VD_DALLAS, + ALC861VD_HP, + ALC861VD_MODEL_LAST, +}; + +#define ALC861VD_DIGOUT_NID 0x06 + +static const hda_nid_t alc861vd_dac_nids[4] = { + /* front, surr, clfe, side surr */ + 0x02, 0x03, 0x04, 0x05 +}; + +/* dac_nids for ALC660vd are in a different order - according to + * Realtek's driver. + * This should probably result in a different mixer for 6stack models + * of ALC660vd codecs, but for now there is only 3stack mixer + * - and it is the same as in 861vd. + * adc_nids in ALC660vd are (is) the same as in 861vd + */ +static const hda_nid_t alc660vd_dac_nids[3] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x04, 0x03 +}; + +static const hda_nid_t alc861vd_adc_nids[1] = { + /* ADC0 */ + 0x09, +}; + +static const hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; + +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ +static const struct hda_input_mux alc861vd_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc861vd_dallas_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_input_mux alc861vd_hp_capture_source = { + .num_items = 2, + .items = { + { "Front Mic", 0x0 }, + { "ATAPI Mic", 0x1 }, + }, +}; + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc861vd_6stack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, 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 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc861vd_6stack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc861vd_6stack_modes[2] = { + { 6, alc861vd_6stack_ch6_init }, + { 8, alc861vd_6stack_ch8_init }, +}; + +static const struct snd_kcontrol_new alc861vd_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 */ +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +static const struct snd_kcontrol_new alc861vd_6st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 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", 0x05, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861vd_3st_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +static const struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/ + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + + { } /* end */ +}; + +/* Pin assignment: Speaker=0x14, HP = 0x15, + * Mic=0x18, Internal Mic = 0x19, CD = 0x1c, PC Beep = 0x1d + */ +static const struct snd_kcontrol_new alc861vd_dallas_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 */ +}; + +/* Pin assignment: Speaker=0x14, Line-out = 0x15, + * Front Mic=0x18, ATAPI Mic = 0x19, + */ +static const struct snd_kcontrol_new alc861vd_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb alc861vd_volume_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {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 + */ + /* 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)}, + + /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x02 - 0x05) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x05, 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 alc861vd_3stack_init_verbs[] = { + /* + * 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}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* 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}, + + { } +}; + +/* + * 6-stack pin configuration: + */ +static const struct hda_verb alc861vd_6stack_init_verbs[] = { + /* + * 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}, + {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}, + + { } +}; + +static const struct hda_verb alc861vd_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc861vd_lenovo_unsol_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_MIC_EVENT}, + {} +}; + +static void alc861vd_lenovo_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc861vd_lenovo_init_hook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc88x_simple_mic_automute(codec); +} + +static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +static const struct hda_verb alc861vd_dallas_verbs[] = { + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x05, 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)}, + {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)}, + + {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_VREF50}, + {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_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_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)}, + + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc861vd_dallas_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * configuration and preset + */ +static const char * const alc861vd_models[ALC861VD_MODEL_LAST] = { + [ALC660VD_3ST] = "3stack-660", + [ALC660VD_3ST_DIG] = "3stack-660-digout", + [ALC660VD_ASUS_V1S] = "asus-v1s", + [ALC861VD_3ST] = "3stack", + [ALC861VD_3ST_DIG] = "3stack-digout", + [ALC861VD_6ST_DIG] = "6stack-digout", + [ALC861VD_LENOVO] = "lenovo", + [ALC861VD_DALLAS] = "dallas", + [ALC861VD_HP] = "hp", + [ALC861VD_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), + SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), + /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ + SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), + SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), + SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), + /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ + SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), + SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), + {} +}; + +static const struct alc_config_preset alc861vd_presets[] = { + [ALC660VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC660VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_6ST_DIG] = { + .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), + .channel_mode = alc861vd_6stack_modes, + .input_mux = &alc861vd_capture_source, + }, + [ALC861VD_LENOVO] = { + .mixers = { alc861vd_lenovo_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs, + alc861vd_eapd_verbs, + alc861vd_lenovo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, + .init_hook = alc861vd_lenovo_init_hook, + }, + [ALC861VD_DALLAS] = { + .mixers = { alc861vd_dallas_mixer }, + .init_verbs = { alc861vd_dallas_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_dallas_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc861vd_dallas_setup, + .init_hook = alc_hp_automute, + }, + [ALC861VD_HP] = { + .mixers = { alc861vd_hp_mixer }, + .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_hp_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc861vd_dallas_setup, + .init_hook = alc_hp_automute, + }, + [ALC660VD_ASUS_V1S] = { + .mixers = { alc861vd_lenovo_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs, + alc861vd_eapd_verbs, + alc861vd_lenovo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, + .init_hook = alc861vd_lenovo_init_hook, + }, +}; + diff --git a/sound/pci/hda/alc880_quirks.c b/sound/pci/hda/alc880_quirks.c new file mode 100644 index 000000000000..c844d2b59988 --- /dev/null +++ b/sound/pci/hda/alc880_quirks.c @@ -0,0 +1,1898 @@ +/* + * 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, + ALC880_LG_LW, + ALC880_MEDION_RIM, +#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; + spec->automute = 1; + spec->automute_mode = 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! + */ + switch (res >> 28) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +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; + spec->automute = 1; + spec->automute_mode = 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! + */ + if ((res >> 28) == ALC_DCVOL_EVENT) + alc880_uniwill_p53_dcvol_automute(codec); + else + alc_sku_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; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * LG LW20 + * + * Pin assignment: + * Speaker-out: 0x14 + * Mic-In: 0x18 + * Built-in Mic-In: 0x19 + * Line-In: 0x1b + * HP-Out: 0x1a + * SPDIF-Out: 0x1e + */ + +static const struct hda_input_mux alc880_lg_lw_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line In", 0x2 }, + }, +}; + +#define alc880_lg_lw_modes alc880_threestack_modes + +static const struct snd_kcontrol_new alc880_lg_lw_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("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("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, 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_lw_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ + + /* set capture source to mic-in */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + /* speaker-out */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* HP-out */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* mic-in to input */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* built-in mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, 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_lw_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc880_medion_rim_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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 Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_input_mux alc880_medion_rim_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_verb alc880_medion_rim_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}, + + /* 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_MUTE}, + /* Internal Speaker */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + { } +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc880_medion_rim_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_hp_automute(codec); + /* toggle EAPD */ + if (spec->jack_present) + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); + else + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); +} + +static void alc880_medion_rim_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! + */ + if ((res >> 28) == ALC_HP_EVENT) + alc880_medion_rim_automute(codec); +} + +static void alc880_medion_rim_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = 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", + [ALC880_LG_LW] = "lg-lw", + [ALC880_MEDION_RIM] = "medion", +#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(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM), + 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, 0x0018, "LG LW20", ALC880_LG_LW), + 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(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), + 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 = alc_sku_unsol_event, + .setup = alc880_lg_setup, + .init_hook = alc_hp_automute, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .loopbacks = alc880_lg_loopbacks, +#endif + }, + [ALC880_LG_LW] = { + .mixers = { alc880_lg_lw_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_lg_lw_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_lg_lw_modes), + .channel_mode = alc880_lg_lw_modes, + .input_mux = &alc880_lg_lw_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc880_lg_lw_setup, + .init_hook = alc_hp_automute, + }, + [ALC880_MEDION_RIM] = { + .mixers = { alc880_medion_rim_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_medion_rim_init_verbs, + alc_gpio2_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_medion_rim_capture_source, + .unsol_event = alc880_medion_rim_unsol_event, + .setup = alc880_medion_rim_setup, + .init_hook = alc880_medion_rim_automute, + }, +#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 new file mode 100644 index 000000000000..617d04723b82 --- /dev/null +++ b/sound/pci/hda/alc882_quirks.c @@ -0,0 +1,3755 @@ +/* + * ALC882/ALC883/ALC888/ALC889 quirk models + * included by patch_realtek.c + */ + +/* ALC882 models */ +enum { + ALC882_AUTO, + ALC882_3ST_DIG, + ALC882_6ST_DIG, + ALC882_ARIMA, + ALC882_W2JC, + ALC882_TARGA, + ALC882_ASUS_A7J, + ALC882_ASUS_A7M, + ALC885_MACPRO, + ALC885_MBA21, + ALC885_MBP3, + ALC885_MB5, + ALC885_MACMINI3, + ALC885_IMAC24, + ALC885_IMAC91, + ALC883_3ST_2ch_DIG, + ALC883_3ST_6ch_DIG, + ALC883_3ST_6ch, + ALC883_6ST_DIG, + ALC883_TARGA_DIG, + ALC883_TARGA_2ch_DIG, + ALC883_TARGA_8ch_DIG, + ALC883_ACER, + ALC883_ACER_ASPIRE, + ALC888_ACER_ASPIRE_4930G, + ALC888_ACER_ASPIRE_6530G, + ALC888_ACER_ASPIRE_8930G, + ALC888_ACER_ASPIRE_7730G, + ALC883_MEDION, + ALC883_MEDION_WIM2160, + ALC883_LAPTOP_EAPD, + ALC883_LENOVO_101E_2ch, + ALC883_LENOVO_NB0763, + ALC888_LENOVO_MS7195_DIG, + ALC888_LENOVO_SKY, + ALC883_HAIER_W66, + ALC888_3ST_HP, + ALC888_6ST_DELL, + ALC883_MITAC, + ALC883_CLEVO_M540R, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, + ALC888_FUJITSU_XA3530, + ALC883_3ST_6ch_INTEL, + ALC889A_INTEL, + ALC889_INTEL, + ALC888_ASUS_M90V, + ALC888_ASUS_EEE1601, + ALC889A_MB31, + ALC1200_ASUS_P5Q, + ALC883_SONY_VAIO_TT, + ALC882_MODEL_LAST, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc888_4ST_ch2_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Line in */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc888_4ST_ch4_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc888_4ST_ch6_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc888_4ST_ch8_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Side */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +static const struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { + { 2, alc888_4ST_ch2_intel_init }, + { 4, alc888_4ST_ch4_intel_init }, + { 6, alc888_4ST_ch6_intel_init }, + { 8, alc888_4ST_ch8_intel_init }, +}; + +/* + * ALC888 Fujitsu Siemens Amillo xa3530 + */ + +static const struct hda_verb alc888_fujitsu_xa3530_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Connect Internal HP to Front */ + {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}, +/* Connect Bass HP to Front */ + {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, 0x00}, +/* Connect Line-Out side jack (SPDIF) to Side */ + {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}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {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}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable unsolicited event for HP jack and Line-out jack */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {} +}; + +static void alc889_automute_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->autocfg.speaker_pins[3] = 0x19; + spec->autocfg.speaker_pins[4] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc889_intel_init_hook(struct hda_codec *codec) +{ + alc889_coef_init(codec); + alc_hp_automute(codec); +} + +static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x17; /* line-out */ + spec->autocfg.hp_pins[1] = 0x1b; /* hp */ + spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ + spec->autocfg.speaker_pins[1] = 0x15; /* bass */ + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* + * ALC888 Acer Aspire 4930G model + */ + +static const struct hda_verb alc888_acer_aspire_4930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal HP to front */ + {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}, +/* Connect HP out to front */ + {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, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * ALC888 Acer Aspire 6530G model + */ + +static const struct hda_verb alc888_acer_aspire_6530g_verbs[] = { +/* Route to built-in subwoofer as well as speakers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, 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)}, +/* Bias voltage on for external mic port */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Enable speaker output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/* Enable headphone output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + *ALC888 Acer Aspire 7730G model + */ + +static const struct hda_verb alc888_acer_aspire_7730G_verbs[] = { +/* Bias voltage on for external mic port */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Enable speaker output */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/* Enable headphone output */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, +/*Enable internal subwoofer */ + {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, 0x02}, + {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + * ALC889 Acer Aspire 8930G model + */ + +static const struct hda_verb alc889_acer_aspire_8930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal Front to Front */ + {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}, +/* Connect Internal Rear to Rear */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect Internal CLFE to CLFE */ + {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}, +/* Connect HP out to Front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable all DACs */ +/* DAC DISABLE/MUTE 1? */ +/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x03}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* DAC DISABLE/MUTE 2? */ +/* some bit here disables the other DACs. Init=0x4900 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* DMIC fix + * This laptop has a stereo digital microphone. The mics are only 1cm apart + * which makes the stereo useless. However, either the mic or the ALC889 + * makes the signal become a difference/sum signal instead of standard + * stereo, which is annoying. So instead we flip this bit which makes the + * codec replicate the sum signal to both channels, turning it into a + * normal mono mic. + */ +/* DMIC_CONTROL? Init value = 0x0001 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0003}, + { } +}; + +static const struct hda_input_mux alc888_2_capture_sources[2] = { + /* Front mic only available on one ADC */ + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + }, + }, + { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, + } +}; + +static const struct hda_input_mux alc888_acer_aspire_6530_sources[2] = { + /* Interal mic only available on one ADC */ + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line In", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + { "Internal Mic", 0xb }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line In", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + } +}; + +static const struct hda_input_mux alc889_capture_sources[3] = { + /* Digital mic only available on first "ADC" */ + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + } +}; + +static const struct snd_kcontrol_new alc888_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_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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_acer_aspire_4930g_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_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc889_acer_aspire_8930g_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("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), + { } /* end */ +}; + + +static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +#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 alc883_adc_nids_alt[1] = { 0x08 }; +static const hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 }; +#define alc889_adc_nids alc880_adc_nids + +static const hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; +static const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; +#define alc883_capsrc_nids alc882_capsrc_nids_alt +static const hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; +#define alc889_capsrc_nids alc882_capsrc_nids + +/* 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 alc889_capture_source = { + .num_items = 3, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x3 }, + { "Line", 0x2 }, + }, +}; + +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 alc883_lenovo_101e_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static const struct hda_input_mux alc883_lenovo_nb0763_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; + +static const struct hda_input_mux alc883_lenovo_sky_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x4 }, + }, +}; + +static const struct hda_input_mux alc883_asus_eee1601_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + }, +}; + +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! */ + }, +}; + +/* + * 2ch mode + */ +static const struct hda_channel_mode alc883_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc882_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, 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 }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc882_3ST_ch4_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, 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 }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc882_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc882_3ST_6ch_modes[3] = { + { 2, alc882_3ST_ch2_init }, + { 4, alc882_3ST_ch4_init }, + { 6, alc882_3ST_ch6_init }, +}; + +#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes + +/* + * 2ch mode + */ +static const struct hda_verb alc883_3ST_ch2_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, 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 }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_3ST_ch4_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, 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 }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_3ST_ch6_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = { + { 2, alc883_3ST_ch2_clevo_init }, + { 4, alc883_3ST_ch4_clevo_init }, + { 6, alc883_3ST_ch6_clevo_init }, +}; + + +/* + * 6ch mode + */ +static const struct hda_verb alc882_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, 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 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc882_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc882_sixstack_modes[2] = { + { 6, alc882_sixstack_ch6_init }, + { 8, alc882_sixstack_ch8_init }, +}; + + +/* 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 + +/* + * 2ch mode + */ +static const struct hda_verb alc883_4ST_ch2_init[] = { + { 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 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_4ST_ch4_init[] = { + { 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 }, + { 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 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_4ST_ch6_init[] = { + { 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_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc883_4ST_ch8_init[] = { + { 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 }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_4ST_8ch_modes[4] = { + { 2, alc883_4ST_ch2_init }, + { 4, alc883_4ST_ch4_init }, + { 6, alc883_4ST_ch6_init }, + { 8, alc883_4ST_ch8_init }, +}; + + +/* + * 2ch mode + */ +static const struct hda_verb alc883_3ST_ch2_intel_init[] = { + { 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 }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc883_3ST_ch4_intel_init[] = { + { 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_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_3ST_ch6_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { + { 2, alc883_3ST_ch2_intel_init }, + { 4, alc883_3ST_ch4_intel_init }, + { 6, alc883_3ST_ch6_intel_init }, +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc889_ch2_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc889_ch6_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 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 alc889_ch8_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 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 alc889_8ch_intel_modes[3] = { + { 2, alc889_ch2_intel_init }, + { 6, alc889_ch6_intel_init }, + { 8, alc889_ch8_intel_init }, +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc883_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, 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 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static const struct hda_verb alc883_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, 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 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc883_sixstack_modes[2] = { + { 6, alc883_sixstack_ch6_init }, + { 8, alc883_sixstack_ch8_init }, +}; + + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +static const struct snd_kcontrol_new alc882_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_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 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 */ +}; + +/* 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_w2jc_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc882_targa_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ??? + * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c + */ +static const struct snd_kcontrol_new alc882_asus_a7j_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 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("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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), + { } /* 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}, + + { } +}; + +static const struct hda_verb alc882_adc1_init_verbs[] = { + /* 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)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } +}; + +static const struct hda_verb alc882_eapd_verbs[] = { + /* change to EAPD mode */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, + { } +}; + +static const struct hda_verb alc889_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static const struct hda_verb alc_hp15_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static const struct hda_verb alc885_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Front HP Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* 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) */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x19, 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% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, 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}, + + /* Mixer elements: 0x18, , 0x1a, 0x1b */ + /* Input mixer1 */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* 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)}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + { } +}; + +static const struct hda_verb alc885_init_input_verbs[] = { + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + { } +}; + + +/* Unmute Selector 24h and set the default input to front mic */ +static const struct hda_verb alc889_init_input_verbs[] = { + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + { } +}; + + +#define alc883_init_verbs alc882_base_init_verbs + +/* Mac Pro test */ +static const struct snd_kcontrol_new alc882_macpro_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + /* FIXME: this looks suspicious... + HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), + */ + { } /* end */ +}; + +static const struct hda_verb alc882_macpro_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)}, + /* Front Pin: output 0 (0x0c) */ + {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, 0x00}, + /* 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}, + /* Speaker: output */ + {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, 0x04}, + /* Headphone output (output 0 - 0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* 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}, + + { } +}; + +/* 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}, + { } +}; + +/* iMac 24 mixer. */ +static const struct snd_kcontrol_new alc885_imac24_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), + { } /* end */ +}; + +/* iMac 24 init verbs. */ +static const struct hda_verb alc885_imac24_init_verbs[] = { + /* Internal speakers: output 0 (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Internal speakers: output 0 (0x0c) */ + {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, 0x00}, + /* Headphone: output 0 (0x0c) */ + {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}, + /* Front Mic: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; + +/* 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; + spec->automute = 1; + spec->automute_mode = 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; + spec->automute = 1; + spec->automute_mode = 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; + spec->automute = 1; + spec->automute_mode = 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; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc882_targa_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc882_targa_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_hp_automute(codec); + snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, + spec->jack_present ? 1 : 3); +} + +static void alc882_targa_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC_HP_EVENT) + alc882_targa_automute(codec); +} + +static const struct hda_verb alc882_asus_a7j_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + { } /* end */ +}; + +static const struct hda_verb alc882_asus_a7m_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ + + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + { } /* end */ +}; + +static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) +{ + unsigned int gpiostate, gpiomask, gpiodir; + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (!muted) + gpiostate |= (1 << pin); + else + gpiostate &= ~(1 << pin); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= (1 << pin); + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= (1 << pin); + + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); + + msleep(1); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); +} + +/* set up GPIO at initialization */ +static void alc885_macpro_init_hook(struct hda_codec *codec) +{ + alc882_gpio_mute(codec, 0, 0); + alc882_gpio_mute(codec, 1, 0); +} + +/* set up GPIO and update auto-muting at initialization */ +static void alc885_imac24_init_hook(struct hda_codec *codec) +{ + alc885_macpro_init_hook(codec); + alc_hp_automute(codec); +} + +/* 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 hda_verb alc883_medion_eapd_verbs[] = { + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, + { } +}; + +#define alc883_base_mixer alc882_base_mixer + +static const struct snd_kcontrol_new alc883_mitac_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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", 0x15, 0x0, HDA_OUTPUT), + 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 alc883_clevo_m720_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_VOLUME("Mic Boost Volume", 0x18, 0, 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_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_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_VOLUME("Mic Boost Volume", 0x18, 0, 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_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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 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 alc883_3ST_6ch_intel_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", 0x15, 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, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc885_8ch_intel_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("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, 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, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x1b, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_fivestack_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 alc883_targa_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker 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("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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_targa_8ch_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front 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_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), + 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("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 alc883_medion_wim2160_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_verb alc883_medion_wim2160_verbs[] = { + /* Unmute front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Set speaker pin to front mixer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Init headphone pin */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_medion_wim2160_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1a; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, 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("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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc888_lenovo_sky_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", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 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("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_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_vaiott_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +static const struct hda_bind_ctls alc883_bind_cap_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct hda_bind_ctls alc883_bind_cap_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), + 0 + }, +}; + +static const struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static const struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* 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 */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_mitac_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc883_mitac_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, */ + + { } /* end */ +}; + +static const struct hda_verb alc883_clevo_m540r_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/ + + /* enable unsolicited event */ + /* + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + */ + + { } /* end */ +}; + +static const struct hda_verb alc883_clevo_m720_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { + /* HP */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_targa_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + +/* Connect Line-Out side jack (SPDIF) to Side */ + {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}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {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}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static const struct hda_verb alc883_lenovo_101e_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_FRONT_EVENT|AC_USRSP_EN}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT|AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc883_lenovo_nb0763_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + { } /* end */ +}; + +static const struct hda_verb alc888_lenovo_ms7195_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_FRONT_EVENT | AC_USRSP_EN}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc883_haier_w66_verbs[] = { + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + { } /* end */ +}; + +static const struct hda_verb alc888_lenovo_sky_verbs[] = { + {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)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static const struct hda_verb alc888_6st_dell_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static const struct hda_verb alc883_vaiott_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static void alc888_3st_hp_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x18; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_3st_hp_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* + * 2ch mode + */ +static const struct hda_verb alc888_3st_hp_2ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static const struct hda_verb alc888_3st_hp_4ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 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, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static const struct hda_verb alc888_3st_hp_6ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 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, 0x01 }, + { } /* end */ +}; + +static const struct hda_channel_mode alc888_3st_hp_modes[3] = { + { 2, alc888_3st_hp_2ch_init }, + { 4, alc888_3st_hp_4ch_init }, + { 6, alc888_3st_hp_6ch_init }, +}; + +static void alc888_lenovo_ms7195_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_lenovo_nb0763_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +#define alc883_targa_init_hook alc882_targa_init_hook +#define alc883_targa_unsol_event alc882_targa_unsol_event + +static void alc883_clevo_m720_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_clevo_m720_init_hook(struct hda_codec *codec) +{ + alc_hp_automute(codec); + alc88x_simple_mic_automute(codec); +} + +static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC_MIC_EVENT: + alc88x_simple_mic_automute(codec); + break; + default: + alc_sku_unsol_event(codec, res); + break; + } +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_haier_w66_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_lenovo_101e_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.line_out_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->automute = 1; + spec->detect_line = 1; + spec->automute_lines = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_acer_aspire_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[1] = 0x16; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc883_acer_eapd_verbs[] = { + /* HP Pin: output 0 (0x0c) */ + {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}, + /* Front Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc888_6st_dell_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc888_lenovo_sky_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + spec->autocfg.speaker_pins[4] = 0x1a; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static void alc883_vaiott_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_asus_m90v_verbs[] = { + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* enable unsolicited event */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_MIC_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static void alc883_mode2_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->ext_mic_pin = 0x18; + spec->int_mic_pin = 0x19; + spec->auto_mic = 1; + spec->automute = 1; + spec->automute_mode = ALC_AUTOMUTE_AMP; +} + +static const struct hda_verb alc888_asus_eee1601_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0838}, + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static void alc883_eee1601_inithook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc_hp_automute(codec); +} + +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 const hda_nid_t alc883_slave_dig_outs[] = { + ALC1200_DIGOUT_NID, 0, +}; + +static const hda_nid_t alc1200_slave_dig_outs[] = { + ALC883_DIGOUT_NID, 0, +}; + +/* + * configuration and preset + */ +static const char * const alc882_models[ALC882_MODEL_LAST] = { + [ALC882_3ST_DIG] = "3stack-dig", + [ALC882_6ST_DIG] = "6stack-dig", + [ALC882_ARIMA] = "arima", + [ALC882_W2JC] = "w2jc", + [ALC882_TARGA] = "targa", + [ALC882_ASUS_A7J] = "asus-a7j", + [ALC882_ASUS_A7M] = "asus-a7m", + [ALC885_MACPRO] = "macpro", + [ALC885_MB5] = "mb5", + [ALC885_MACMINI3] = "macmini3", + [ALC885_MBA21] = "mba21", + [ALC885_MBP3] = "mbp3", + [ALC885_IMAC24] = "imac24", + [ALC885_IMAC91] = "imac91", + [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", + [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", + [ALC883_3ST_6ch] = "3stack-6ch", + [ALC883_6ST_DIG] = "alc883-6stack-dig", + [ALC883_TARGA_DIG] = "targa-dig", + [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", + [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", + [ALC883_ACER] = "acer", + [ALC883_ACER_ASPIRE] = "acer-aspire", + [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", + [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g", + [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", + [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", + [ALC883_MEDION] = "medion", + [ALC883_MEDION_WIM2160] = "medion-wim2160", + [ALC883_LAPTOP_EAPD] = "laptop-eapd", + [ALC883_LENOVO_101E_2ch] = "lenovo-101e", + [ALC883_LENOVO_NB0763] = "lenovo-nb0763", + [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", + [ALC888_LENOVO_SKY] = "lenovo-sky", + [ALC883_HAIER_W66] = "haier-w66", + [ALC888_3ST_HP] = "3stack-hp", + [ALC888_6ST_DELL] = "6stack-dell", + [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M540R] = "clevo-m540r", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", + [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", + [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", + [ALC889A_INTEL] = "intel-alc889a", + [ALC889_INTEL] = "intel-x58", + [ALC1200_ASUS_P5Q] = "asus-p5q", + [ALC889A_MB31] = "mb31", + [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", + [ALC882_AUTO] = "auto", +}; + +static const struct snd_pci_quirk alc882_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), + + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", + ALC888_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", + ALC888_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", + ALC888_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", + ALC888_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO), + SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO), + SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", + ALC888_ACER_ASPIRE_6530G), + SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", + ALC888_ACER_ASPIRE_6530G), + SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", + ALC888_ACER_ASPIRE_7730G), + /* default Acer -- disabled as it causes more problems. + * model=auto should work fine now + */ + /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */ + + SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), + + SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavilion", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), + + SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), + SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), + SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), + SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), + SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), + SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), + + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), + SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), + SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), + + SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ + SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), + + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), + /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */ + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", + ALC883_FUJITSU_PI2515), + SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx", + ALC888_FUJITSU_XA3530), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), + SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY), + SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), + SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), + + SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), + SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), + SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), + SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG), + + {} +}; + +/* 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, 0x0c00, "Mac Pro", ALC885_MACPRO), + SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31), + SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M), + 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, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), + 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[] = { + [ALC882_3ST_DIG] = { + .mixers = { alc882_base_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_6ST_DIG] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_ARIMA] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_W2JC] = { + .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + }, + [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 = alc_sku_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 = alc_sku_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 = alc_sku_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 = alc_sku_unsol_event, + .setup = alc885_macmini3_setup, + .init_hook = alc_hp_automute, + }, + [ALC885_MACPRO] = { + .mixers = { alc882_macpro_mixer }, + .init_verbs = { alc882_macpro_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .init_hook = alc885_macpro_init_hook, + }, + [ALC885_IMAC24] = { + .mixers = { alc885_imac24_mixer }, + .init_verbs = { alc885_imac24_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc885_imac24_setup, + .init_hook = alc885_imac24_init_hook, + }, + [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 = alc_sku_unsol_event, + .setup = alc885_imac91_setup, + .init_hook = alc_hp_automute, + }, + [ALC882_TARGA] = { + .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc880_gpio3_init_verbs, alc882_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC882_ASUS_A7J] = { + .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_asus_a7j_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_ASUS_A7M] = { + .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs, + alc882_asus_a7m_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC883_3ST_2ch_DIG] = { + .mixers = { alc883_3ST_2ch_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_INTEL] = { + .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), + .channel_mode = alc883_3ST_6ch_intel_modes, + .need_dac_fix = 1, + .input_mux = &alc883_3stack_6ch_intel, + }, + [ALC889A_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc885_init_input_verbs, + alc_hp15_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc_hp_automute, + .unsol_event = alc_sku_unsol_event, + .need_dac_fix = 1, + }, + [ALC889_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc889_init_input_verbs, + alc889_eapd_verbs, alc_hp15_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc889_intel_init_hook, + .unsol_event = alc_sku_unsol_event, + .need_dac_fix = 1, + }, + [ALC883_6ST_DIG] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_TARGA_DIG] = { + .mixers = { alc883_targa_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_TARGA_2ch_DIG] = { + .mixers = { alc883_targa_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_TARGA_8ch_DIG] = { + .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_targa_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes), + .channel_mode = alc883_4ST_8ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC883_ACER] = { + .mixers = { alc883_base_mixer }, + /* 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. + */ + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_ACER_ASPIRE] = { + .mixers = { alc883_acer_aspire_mixer }, + .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_acer_aspire_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_4930G] = { + .mixers = { alc888_acer_aspire_4930g_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_4930g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_4930g_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_6530G] = { + .mixers = { alc888_acer_aspire_6530_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_6530g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_acer_aspire_6530_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_6530g_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ACER_ASPIRE_8930G] = { + .mixers = { alc889_acer_aspire_8930g_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc889_acer_aspire_8930g_verbs, + alc889_eapd_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .capsrc_nids = alc889_capsrc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .num_mux_defs = + ARRAY_SIZE(alc889_capture_sources), + .input_mux = alc889_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc889_acer_aspire_8930g_setup, + .init_hook = alc_hp_automute, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .power_hook = alc_power_eapd, +#endif + }, + [ALC888_ACER_ASPIRE_7730G] = { + .mixers = { alc883_3ST_6ch_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_7730G_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_acer_aspire_7730g_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_MEDION] = { + .mixers = { alc883_fivestack_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_medion_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_MEDION_WIM2160] = { + .mixers = { alc883_medion_wim2160_mixer }, + .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_medion_wim2160_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_LAPTOP_EAPD] = { + .mixers = { alc883_base_mixer }, + .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_CLEVO_M540R] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes), + .channel_mode = alc883_3ST_6ch_clevo_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + /* This machine has the hardware HP auto-muting, thus + * we need no software mute via unsol event + */ + }, + [ALC883_CLEVO_M720] = { + .mixers = { alc883_clevo_m720_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_clevo_m720_unsol_event, + .setup = alc883_clevo_m720_setup, + .init_hook = alc883_clevo_m720_init_hook, + }, + [ALC883_LENOVO_101E_2ch] = { + .mixers = { alc883_lenovo_101e_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_lenovo_101e_capture_source, + .setup = alc883_lenovo_101e_setup, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc_inithook, + }, + [ALC883_LENOVO_NB0763] = { + .mixers = { alc883_lenovo_nb0763_mixer }, + .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_lenovo_nb0763_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_lenovo_nb0763_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_LENOVO_MS7195_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_lenovo_ms7195_setup, + .init_hook = alc_inithook, + }, + [ALC883_HAIER_W66] = { + .mixers = { alc883_targa_2ch_mixer}, + .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_haier_w66_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_3ST_HP] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), + .channel_mode = alc888_3st_hp_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_3st_hp_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_6ST_DELL] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_6st_dell_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_MITAC] = { + .mixers = { alc883_mitac_mixer }, + .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_mitac_setup, + .init_hook = alc_hp_automute, + }, + [ALC883_FUJITSU_PI2515] = { + .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_2ch_fujitsu_pi2515_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_2ch_fujitsu_pi2515_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_FUJITSU_XA3530] = { + .mixers = { alc888_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc888_fujitsu_xa3530_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), + .channel_mode = alc888_4ST_8ch_intel_modes, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_fujitsu_xa3530_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_LENOVO_SKY] = { + .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .need_dac_fix = 1, + .input_mux = &alc883_lenovo_sky_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc888_lenovo_sky_setup, + .init_hook = alc_hp_automute, + }, + [ALC888_ASUS_M90V] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_mode2_setup, + .init_hook = alc_inithook, + }, + [ALC888_ASUS_EEE1601] = { + .mixers = { alc883_asus_eee1601_mixer }, + .cap_mixer = alc883_asus_eee1601_cap_mixer, + .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_asus_eee1601_capture_source, + .unsol_event = alc_sku_unsol_event, + .init_hook = alc883_eee1601_inithook, + }, + [ALC1200_ASUS_P5Q] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC1200_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc1200_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [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, + }, + [ALC883_SONY_VAIO_TT] = { + .mixers = { alc883_vaiott_mixer }, + .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_vaiott_setup, + .init_hook = alc_hp_automute, + }, +}; + + diff --git a/sound/pci/hda/alc_quirks.c b/sound/pci/hda/alc_quirks.c new file mode 100644 index 000000000000..2be1129cf458 --- /dev/null +++ b/sound/pci/hda/alc_quirks.c @@ -0,0 +1,467 @@ +/* + * 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); +} + + +/* 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 45b4a8d70e08..3e7850c238c3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -91,8 +91,10 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); static void hda_keep_power_on(struct hda_codec *codec); +#define hda_codec_is_power_on(codec) ((codec)->power_on) #else static inline void hda_keep_power_on(struct hda_codec *codec) {} +#define hda_codec_is_power_on(codec) 1 #endif /** @@ -243,7 +245,8 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, { unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); unsigned int res; - codec_exec_verb(codec, cmd, &res); + if (codec_exec_verb(codec, cmd, &res)) + return -1; return res; } EXPORT_SYMBOL_HDA(snd_hda_codec_read); @@ -307,63 +310,107 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); -static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); -static bool add_conn_list(struct snd_array *array, hda_nid_t nid); -static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst, - hda_nid_t *src, int len); +/* look up the cached results */ +static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) +{ + int i, len; + for (i = 0; i < array->used; ) { + hda_nid_t *p = snd_array_elem(array, i); + if (nid == *p) + return p; + len = p[1]; + i += len + 2; + } + return NULL; +} /** - * snd_hda_get_connections - get connection list + * snd_hda_get_conn_list - get connection list * @codec: the HDA codec * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store + * @listp: the pointer to store NID list * * Parses the connection list of the given widget and stores the list * of NIDs. * * Returns the number of connections, or a negative error code. */ -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) { struct snd_array *array = &codec->conn_lists; - int i, len, old_used; + int len, err; hda_nid_t list[HDA_MAX_CONNECTIONS]; + hda_nid_t *p; + bool added = false; - /* look up the cached results */ - for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); - len = p[1]; - if (nid == *p) - return copy_conn_list(nid, conn_list, max_conns, - p + 2, len); - i += len + 2; + again: + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(array, nid); + if (p) { + if (listp) + *listp = p + 2; + return p[1]; } + if (snd_BUG_ON(added)) + return -EINVAL; - len = _hda_get_connections(codec, nid, list, HDA_MAX_CONNECTIONS); + /* read the connection and add to the cache */ + len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS); if (len < 0) return len; + err = snd_hda_override_conn_list(codec, nid, len, list); + if (err < 0) + return err; + added = true; + goto again; +} +EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); - /* add to the cache */ - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; +/** + * snd_hda_get_connections - copy connection list + * @codec: the HDA codec + * @nid: NID to parse + * @conn_list: connection list array + * @max_conns: max. number of connections to store + * + * Parses the connection list of the given widget and stores the list + * of NIDs. + * + * Returns the number of connections, or a negative error code. + */ +int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list); - return copy_conn_list(nid, conn_list, max_conns, list, len); - - error_add: - array->used = old_used; - return -ENOMEM; + if (len <= 0) + return len; + if (len > max_conns) { + snd_printk(KERN_ERR "hda_codec: " + "Too many connections %d for NID 0x%x\n", + len, nid); + return -EINVAL; + } + memcpy(conn_list, list, len * sizeof(hda_nid_t)); + return len; } EXPORT_SYMBOL_HDA(snd_hda_get_connections); -static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) +/** + * snd_hda_get_raw_connections - copy connection list without cache + * @codec: the HDA codec + * @nid: NID to parse + * @conn_list: connection list array + * @max_conns: max. number of connections to store + * + * Like snd_hda_get_connections(), copy the connection list but without + * checking through the connection-list cache. + * Currently called only from hda_proc.c, so not exported. + */ +int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) { unsigned int parm; int i, conn_len, conns; @@ -376,11 +423,8 @@ static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid, wcaps = get_wcaps(codec, nid); if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) { - snd_printk(KERN_WARNING "hda_codec: " - "connection list not available for 0x%x\n", nid); - return -EINVAL; - } + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { @@ -470,18 +514,77 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) return true; } -static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst, - hda_nid_t *src, int len) +/** + * snd_hda_override_conn_list - add/modify the connection-list to cache + * @codec: the HDA codec + * @nid: NID to parse + * @len: number of connection list entries + * @list: the list of connection entries + * + * Add or modify the given connection-list to the cache. If the corresponding + * cache already exists, invalidate it and append a new one. + * + * Returns zero or a negative error code. + */ +int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, + const hda_nid_t *list) { - if (len > max_dst) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - len, nid); - return -EINVAL; + struct snd_array *array = &codec->conn_lists; + hda_nid_t *p; + int i, old_used; + + p = lookup_conn_list(array, nid); + if (p) + *p = -1; /* invalidate the old entry */ + + old_used = array->used; + if (!add_conn_list(array, nid) || !add_conn_list(array, len)) + goto error_add; + for (i = 0; i < len; i++) + if (!add_conn_list(array, list[i])) + goto error_add; + return 0; + + error_add: + array->used = old_used; + return -ENOMEM; +} +EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); + +/** + * snd_hda_get_conn_index - get the connection index of the given NID + * @codec: the HDA codec + * @mux: NID containing the list + * @nid: NID to select + * @recursive: 1 when searching NID recursively, otherwise 0 + * + * Parses the connection list of the widget @mux and checks whether the + * widget @nid is present. If it is, return the connection index. + * Otherwise it returns -1. + */ +int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid, int recursive) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + if (!recursive) + return -1; + if (recursive > 5) { + snd_printd("hda_codec: too deep connection for 0x%x\n", nid); + return -1; } - memcpy(dst, src, len * sizeof(hda_nid_t)); - return len; + recursive++; + for (i = 0; i < nums; i++) + if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) + return i; + return -1; } +EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); /** * snd_hda_queue_unsol_event - add an unsolicited event to queue @@ -1000,7 +1103,7 @@ void snd_hda_shutup_pins(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ static void restore_shutup_pins(struct hda_codec *codec) { @@ -1083,6 +1186,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->conn_lists); + snd_array_free(&codec->spdif_out); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1144,6 +1248,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); + snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { @@ -1396,7 +1501,7 @@ static void purify_inactive_streams(struct hda_codec *codec) } } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* clean up all streams; called from suspend */ static void hda_cleanup_all_streams(struct hda_codec *codec) { @@ -1735,7 +1840,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -1765,7 +1870,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) } } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) @@ -2555,11 +2660,13 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff; - ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff; + ucontrol->value.iec958.status[0] = spdif->status & 0xff; + ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; return 0; } @@ -2644,23 +2751,23 @@ static int snd_hda_spdif_default_put(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; + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + hda_nid_t nid = spdif->nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); - codec->spdif_status = ucontrol->value.iec958.status[0] | + spdif->status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | ((unsigned int)ucontrol->value.iec958.status[3] << 24); - val = convert_from_spdif_status(codec->spdif_status); - val |= codec->spdif_ctls & 1; - change = codec->spdif_ctls != val; - codec->spdif_ctls = val; - - if (change) + val = convert_from_spdif_status(spdif->status); + val |= spdif->ctls & 1; + change = spdif->ctls != val; + spdif->ctls = val; + if (change && nid != (u16)-1) set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); - mutex_unlock(&codec->spdif_mutex); return change; } @@ -2671,33 +2778,42 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE; + ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; return 0; } +static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, + int dig1, int dig2) +{ + set_dig_out_convert(codec, nid, dig1, dig2); + /* unmute amp switch (if any) */ + if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && + (dig1 & AC_DIG1_ENABLE)) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); +} + static int snd_hda_spdif_out_switch_put(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; + int idx = kcontrol->private_value; + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + hda_nid_t nid = spdif->nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); - val = codec->spdif_ctls & ~AC_DIG1_ENABLE; + val = spdif->ctls & ~AC_DIG1_ENABLE; if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; - change = codec->spdif_ctls != val; - if (change) { - codec->spdif_ctls = val; - set_dig_out_convert(codec, nid, val & 0xff, -1); - /* unmute amp switch (if any) */ - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (val & AC_DIG1_ENABLE)) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - } + change = spdif->ctls != val; + spdif->ctls = val; + if (change && nid != (u16)-1) + set_spdif_ctls(codec, nid, val & 0xff, -1); mutex_unlock(&codec->spdif_mutex); return change; } @@ -2744,36 +2860,79 @@ static struct snd_kcontrol_new dig_mixes[] = { * * Returns 0 if successful, or a negative error code. */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid) { int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; int idx; + struct hda_spdif_out *spdif; idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch"); if (idx < 0) { printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); return -EBUSY; } + spdif = snd_array_new(&codec->spdif_out); for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) return -ENOMEM; kctl->id.index = idx; - kctl->private_value = nid; - err = snd_hda_ctl_add(codec, nid, kctl); + kctl->private_value = codec->spdif_out.used - 1; + err = snd_hda_ctl_add(codec, associated_nid, kctl); if (err < 0) return err; } - codec->spdif_ctls = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); + spdif->nid = cvt_nid; + spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); +struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, + hda_nid_t nid) +{ + int i; + for (i = 0; i < codec->spdif_out.used; i++) { + struct hda_spdif_out *spdif = + snd_array_elem(&codec->spdif_out, i); + if (spdif->nid == nid) + return spdif; + } + return NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); + +void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) +{ + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + + mutex_lock(&codec->spdif_mutex); + spdif->nid = (u16)-1; + mutex_unlock(&codec->spdif_mutex); +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); + +void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) +{ + struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + unsigned short val; + + mutex_lock(&codec->spdif_mutex); + if (spdif->nid != nid) { + spdif->nid = nid; + val = spdif->ctls; + set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); + } + mutex_unlock(&codec->spdif_mutex); +} +EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign); + /* * SPDIF sharing with analog output */ @@ -2925,7 +3084,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* * command cache */ @@ -3042,53 +3201,32 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ -/* - * set power state of the codec - */ -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) +void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state, + bool eapd_workaround) { - hda_nid_t nid; + hda_nid_t nid = codec->start_nid; int i; - /* this delay seems necessary to avoid click noise at power-down */ - if (power_state == AC_PWRST_D3) - msleep(100); - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - /* partial workaround for "azx_get_response timeout" */ - if (power_state == AC_PWRST_D0 && - (codec->vendor_id & 0xffff0000) == 0x14f10000) - msleep(10); - - nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); - if (wcaps & AC_WCAP_POWER) { - unsigned int wid_type = get_wcaps_type(wcaps); - if (power_state == AC_PWRST_D3 && - wid_type == AC_WID_PIN) { - unsigned int pincap; - /* - * don't power down the widget if it controls - * eapd and EAPD_BTLENABLE is set. - */ - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_EAPD) { - int eapd = snd_hda_codec_read(codec, - nid, 0, + if (!(wcaps & AC_WCAP_POWER)) + continue; + /* don't power down the widget if it controls eapd and + * EAPD_BTLENABLE is set. + */ + if (eapd_workaround && power_state == AC_PWRST_D3 && + get_wcaps_type(wcaps) == AC_WID_PIN && + (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { + int eapd = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_EAPD_BTLENABLE, 0); - eapd &= 0x02; - if (eapd) - continue; - } - } - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - power_state); + if (eapd & 0x02) + continue; } + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, + power_state); } if (power_state == AC_PWRST_D0) { @@ -3105,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, } while (time_after_eq(end_time, jiffies)); } } +EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); + +/* + * set power state of the codec + */ +static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + if (codec->patch_ops.set_power_state) { + codec->patch_ops.set_power_state(codec, fg, power_state); + return; + } + + /* this delay seems necessary to avoid click noise at power-down */ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} #ifdef CONFIG_SND_HDA_HWDEP /* execute additional init verbs */ @@ -3117,7 +3275,7 @@ static void hda_exec_init_verbs(struct hda_codec *codec) static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* * call suspend and power-down; used both from PM and power-save */ @@ -3158,7 +3316,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_codec_resume_cache(codec); } } -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ /** @@ -3356,7 +3514,7 @@ static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) * * Returns 0 if successful, otherwise a negative error code. */ -static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { unsigned int i, val, wcaps; @@ -3448,6 +3606,7 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, return 0; } +EXPORT_SYMBOL_HDA(snd_hda_query_supported_pcm); /** * snd_hda_is_supported_format - Check the validity of the format @@ -3913,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); #ifdef CONFIG_SND_HDA_POWER_SAVE -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state); - static void hda_power_work(struct work_struct *work) { struct hda_codec *codec = @@ -4177,10 +4333,12 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) { + struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid); + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) set_dig_out_convert(codec, nid, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff, -1); snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); if (codec->slave_dig_outs) { @@ -4190,9 +4348,9 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, format); } /* turn on again (if needed) */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) set_dig_out_convert(codec, nid, - codec->spdif_ctls & 0xff, -1); + spdif->ctls & 0xff, -1); } static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) @@ -4216,11 +4374,8 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) if (!bus) return; list_for_each_entry(codec, &bus->codec_list, list) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!codec->power_on) - continue; -#endif - if (codec->patch_ops.reboot_notify) + if (hda_codec_is_power_on(codec) && + codec->patch_ops.reboot_notify) codec->patch_ops.reboot_notify(codec); } } @@ -4348,6 +4503,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, { const hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); int i; mutex_lock(&codec->spdif_mutex); @@ -4356,7 +4513,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && - !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { + !(spdif->status & IEC958_AES0_NONAUDIO)) { mout->dig_out_used = HDA_DIG_ANALOG_DUP; setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); @@ -4528,7 +4685,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; - short assoc, loc; + short assoc, loc, conn, dev; /* read all default configuration for pin complex */ if (wid_type != AC_WID_PIN) @@ -4538,10 +4695,19 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, continue; def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) continue; loc = get_defcfg_location(def_conf); - switch (get_defcfg_device(def_conf)) { + dev = get_defcfg_device(def_conf); + + /* workaround for buggy BIOS setups */ + if (dev == AC_JACK_LINE_OUT) { + if (conn == AC_JACK_PORT_FIXED) + dev = AC_JACK_SPEAKER; + } + + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); @@ -4908,11 +5074,10 @@ int snd_hda_suspend(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!codec->power_on) - continue; -#endif - hda_call_codec_suspend(codec); + if (hda_codec_is_power_on(codec)) + hda_call_codec_suspend(codec); + if (codec->patch_ops.post_suspend) + codec->patch_ops.post_suspend(codec); } return 0; } @@ -4932,6 +5097,8 @@ int snd_hda_resume(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { + if (codec->patch_ops.pre_resume) + codec->patch_ops.pre_resume(codec); if (snd_hda_codec_needs_resume(codec)) hda_call_codec_resume(codec); } @@ -4957,17 +5124,15 @@ void *snd_array_new(struct snd_array *array) { if (array->used >= array->alloced) { int num = array->alloced + array->alloc_align; + int size = (num + 1) * array->elem_size; + int oldsize = array->alloced * array->elem_size; void *nlist; if (snd_BUG_ON(num >= 4096)) return NULL; - nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL); + nlist = krealloc(array->list, size, GFP_KERNEL); if (!nlist) return NULL; - if (array->list) { - memcpy(nlist, array->list, - array->elem_size * array->alloced); - kfree(array->list); - } + memset(nlist + oldsize, 0, size - oldsize); array->list = nlist; array->alloced = num; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 59c97306c1de..755f2b0f9d8e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -26,10 +26,6 @@ #include <sound/pcm.h> #include <sound/hwdep.h> -#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE) -#define SND_HDA_NEEDS_RESUME /* resume control code is required */ -#endif - /* * nodes */ @@ -704,8 +700,12 @@ struct hda_codec_ops { int (*init)(struct hda_codec *codec); void (*free)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); -#ifdef SND_HDA_NEEDS_RESUME + void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state); +#ifdef CONFIG_PM int (*suspend)(struct hda_codec *codec, pm_message_t state); + int (*post_suspend)(struct hda_codec *codec); + int (*pre_resume)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -829,8 +829,7 @@ struct hda_codec { struct mutex spdif_mutex; struct mutex control_mutex; - unsigned int spdif_status; /* IEC958 status bits */ - unsigned short spdif_ctls; /* SPDIF control bits */ + struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ struct snd_array init_pins; /* initial (BIOS) pin configurations */ @@ -904,6 +903,16 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns); +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp); +int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, + const hda_nid_t *list); +int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid, int recursive); +int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp); struct hda_verb { hda_nid_t nid; @@ -918,7 +927,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); /* cached write */ -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, @@ -947,6 +956,17 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg); /* for hwdep */ void snd_hda_shutup_pins(struct hda_codec *codec); +/* SPDIF controls */ +struct hda_spdif_out { + hda_nid_t nid; /* Converter nid values relate to */ + unsigned int status; /* IEC958 status bits */ + unsigned short ctls; /* SPDIF control bits */ +}; +struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, + hda_nid_t nid); +void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx); +void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); + /* * Mixer */ @@ -988,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); +void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state, + bool eapd_workaround); /* * power management @@ -997,17 +1020,15 @@ int snd_hda_suspend(struct hda_bus *bus); int snd_hda_resume(struct hda_bus *bus); #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) { +#ifdef CONFIG_SND_HDA_POWER_SAVE if (codec->patch_ops.check_power_status) return codec->patch_ops.check_power_status(codec, nid); +#endif return 0; } -#else -#define hda_call_check_power_status(codec, nid) 0 -#endif /* * get widget information diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index e3e853153d14..28ce17d09c33 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -580,43 +580,45 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) #endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ -void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, - struct hda_pcm_stream *codec_pars) +void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, + struct hda_pcm_stream *hinfo) { + u32 rates; + u64 formats; + unsigned int maxbps; + unsigned int channels_max; int i; /* assume basic audio support (the basic audio flag is not in ELD; * however, all audio capable sinks are required to support basic * audio) */ - pcm->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; - pcm->formats = SNDRV_PCM_FMTBIT_S16_LE; - pcm->maxbps = 16; - pcm->channels_max = 2; + rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000; + formats = SNDRV_PCM_FMTBIT_S16_LE; + maxbps = 16; + channels_max = 2; for (i = 0; i < eld->sad_count; i++) { struct cea_sad *a = &eld->sad[i]; - pcm->rates |= a->rates; - if (a->channels > pcm->channels_max) - pcm->channels_max = a->channels; + rates |= a->rates; + if (a->channels > channels_max) + channels_max = a->channels; if (a->format == AUDIO_CODING_TYPE_LPCM) { if (a->sample_bits & AC_SUPPCM_BITS_20) { - pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (pcm->maxbps < 20) - pcm->maxbps = 20; + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (maxbps < 20) + maxbps = 20; } if (a->sample_bits & AC_SUPPCM_BITS_24) { - pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (pcm->maxbps < 24) - pcm->maxbps = 24; + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (maxbps < 24) + maxbps = 24; } } } - if (!codec_pars) - return; - /* restrict the parameters by the values the codec provides */ - pcm->rates &= codec_pars->rates; - pcm->formats &= codec_pars->formats; - pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max); - pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps); + hinfo->rates &= rates; + hinfo->formats &= formats; + hinfo->maxbps = min(hinfo->maxbps, maxbps); + hinfo->channels_max = min(hinfo->channels_max, channels_max); } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 486f6deb3eee..be6982289c0d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -177,7 +177,8 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTSTS 0x24 #define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ -#define ICH6_REG_SYNC 0x34 +#define ICH6_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ +#define ICH6_REG_SSYNC 0x38 #define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBUBASE 0x44 #define ICH6_REG_CORBWP 0x48 @@ -479,6 +480,7 @@ enum { #define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ #define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ +#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -1706,13 +1708,16 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; unsigned int bufsize, period_bytes, format_val, stream_tag; int err; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); + unsigned short ctls = spdif ? spdif->ctls : 0; azx_stream_reset(chip, azx_dev); format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, hinfo->maxbps, - apcm->codec->spdif_ctls); + ctls); if (!format_val) { snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", @@ -1792,7 +1797,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->reg_lock); if (nsync > 1) { /* first, set SYNC bits of corresponding streams */ - azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) | sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) @@ -1848,7 +1857,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (nsync > 1) { spin_lock(&chip->reg_lock); /* reset SYNC bits */ - azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + azx_writel(chip, OLD_SSYNC, + azx_readl(chip, OLD_SSYNC) & ~sbits); + else + azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); spin_unlock(&chip->reg_lock); } return 0; @@ -1863,7 +1876,7 @@ static unsigned int azx_via_get_position(struct azx *chip, unsigned int fifo_size; link_pos = azx_sd_readl(azx_dev, SD_LPIB); - if (azx_dev->index >= 4) { + if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Playback, no problem using link position */ return link_pos; } @@ -1927,6 +1940,17 @@ static unsigned int azx_get_position(struct azx *chip, default: /* use the position buffer */ pos = le32_to_cpu(*azx_dev->posbuf); + if (chip->position_fix[stream] == POS_FIX_AUTO) { + if (!pos || pos == (u32)-1) { + printk(KERN_WARNING + "hda-intel: Invalid position buffer, " + "using LPIB read method instead.\n"); + chip->position_fix[stream] = POS_FIX_LPIB; + pos = azx_sd_readl(azx_dev, SD_LPIB); + } else + chip->position_fix[stream] = POS_FIX_POSBUF; + } + break; } if (pos >= azx_dev->bufsize) @@ -1964,16 +1988,6 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) stream = azx_dev->substream->stream; pos = azx_get_position(chip, azx_dev); - if (chip->position_fix[stream] == POS_FIX_AUTO) { - if (!pos) { - printk(KERN_WARNING - "hda-intel: Invalid position buffer, " - "using LPIB read method instead.\n"); - chip->position_fix[stream] = POS_FIX_LPIB; - pos = azx_get_position(chip, azx_dev); - } else - chip->position_fix[stream] = POS_FIX_POSBUF; - } if (WARN_ONCE(!azx_dev->period_bytes, "hda-intel: zero azx_dev->period_bytes")) @@ -2061,6 +2075,8 @@ static void azx_pcm_free(struct snd_pcm *pcm) } } +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *cpcm) @@ -2069,6 +2085,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, struct snd_pcm *pcm; struct azx_pcm *apcm; int pcm_dev = cpcm->device; + unsigned int size; int s, err; if (pcm_dev >= HDA_MAX_PCMS) { @@ -2104,9 +2121,12 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, snd_pcm_set_ops(pcm, s, &azx_pcm_ops); } /* buffer pre-allocation */ + size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; + if (size > MAX_PREALLOC_SIZE) + size = MAX_PREALLOC_SIZE; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), - 1024 * 64, 32 * 1024 * 1024); + size, MAX_PREALLOC_SIZE); return 0; } @@ -2149,7 +2169,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) { if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, - "hda_intel", chip)) { + KBUILD_MODNAME, chip)) { printk(KERN_ERR "hda-intel: unable to grab IRQ %d, " "disabling device\n", chip->pci->irq); if (do_disconnect) @@ -2347,28 +2367,20 @@ static int azx_dev_free(struct snd_device *device) * white/black-listing for position_fix */ static struct snd_pci_quirk position_fix_list[] __devinitdata = { - SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1025, 0x026f, "Acer Aspire 5538", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell Inspiron 1120", POS_FIX_LPIB), SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x8410, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB), SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB), - SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), {} }; @@ -2815,6 +2827,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP }, + { PCI_DEVICE(0x8086, 0x2668), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH6 */ + { PCI_DEVICE(0x8086, 0x27d8), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH7 */ + { PCI_DEVICE(0x8086, 0x269a), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ESB2 */ + { PCI_DEVICE(0x8086, 0x284b), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH8 */ + { PCI_DEVICE(0x8086, 0x293e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH9 */ + { PCI_DEVICE(0x8086, 0x293f), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH9 */ + { PCI_DEVICE(0x8086, 0x3a3e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH10 */ + { PCI_DEVICE(0x8086, 0x3a6e), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC }, /* ICH10 */ /* Generic Intel */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, @@ -2908,7 +2936,7 @@ MODULE_DEVICE_TABLE(pci, azx_ids); /* pci_driver definition */ static struct pci_driver driver = { - .name = "HDA Intel", + .name = KBUILD_MODNAME, .id_table = azx_ids, .probe = azx_probe, .remove = __devexit_p(azx_remove), diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 08ec073444e2..2e7ac31afa8d 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -131,7 +131,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM void snd_hda_codec_resume_amp(struct hda_codec *codec); #endif @@ -212,7 +212,9 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, /* * SPDIF I/O */ -int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, + hda_nid_t associated_nid, + hda_nid_t cvt_nid); int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); /* @@ -563,7 +565,6 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) * power-management */ -#ifdef CONFIG_SND_HDA_POWER_SAVE void snd_hda_schedule_power_save(struct hda_codec *codec); struct hda_amp_list { @@ -580,7 +581,6 @@ struct hda_loopback_check { int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid); -#endif /* CONFIG_SND_HDA_POWER_SAVE */ /* * AMP control callbacks @@ -639,8 +639,8 @@ struct hdmi_eld { int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); void snd_hdmi_show_eld(struct hdmi_eld *eld); -void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm, - struct hda_pcm_stream *codec_pars); +void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, + struct hda_pcm_stream *hinfo); #ifdef CONFIG_PROC_FS int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index bfe74c2fb079..2be57b051aa2 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -636,7 +636,7 @@ static void print_codec_info(struct snd_info_entry *entry, wid_caps |= AC_WCAP_CONN_LIST; if (wid_caps & AC_WCAP_CONN_LIST) - conn_len = snd_hda_get_connections(codec, nid, conn, + conn_len = snd_hda_get_raw_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); if (wid_caps & AC_WCAP_IN_AMP) { diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d694e9d4921d..8648917acffb 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -213,7 +213,9 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, @@ -561,7 +563,7 @@ static void ad198x_free(struct hda_codec *codec) snd_hda_detach_beep_device(codec); } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) { ad198x_shutup(codec); @@ -577,7 +579,7 @@ static const struct hda_codec_ops ad198x_patch_ops = { #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = ad198x_check_power_status, #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .suspend = ad198x_suspend, #endif .reboot_notify = ad198x_shutup, @@ -1920,7 +1922,8 @@ static int patch_ad1981(struct hda_codec *codec) spec->mixers[0] = ad1981_hp_mixers; spec->num_init_verbs = 2; spec->init_verbs[1] = ad1981_hp_init_verbs; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x0a)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1981_hp_capture_source; codec->patch_ops.init = ad1981_hp_init; diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 61b92634b161..6b406840846e 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -240,7 +240,8 @@ static int ca0110_build_controls(struct hda_codec *codec) } if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c new file mode 100644 index 000000000000..d9a2254ceef6 --- /dev/null +++ b/sound/pci/hda/patch_ca0132.c @@ -0,0 +1,1097 @@ +/* + * HD audio interface patch for Creative CA0132 chip + * + * Copyright (c) 2011, Creative Technology Ltd. + * + * Based on patch_ca0110.c + * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +#define WIDGET_CHIP_CTRL 0x15 +#define WIDGET_DSP_CTRL 0x16 + +#define WUH_MEM_CONNID 10 +#define DSP_MEM_CONNID 16 + +enum hda_cmd_vendor_io { + /* for DspIO node */ + VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, + VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100, + + VENDOR_DSPIO_STATUS = 0xF01, + VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702, + VENDOR_DSPIO_SCP_READ_DATA = 0xF02, + VENDOR_DSPIO_DSP_INIT = 0x703, + VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704, + VENDOR_DSPIO_SCP_READ_COUNT = 0xF04, + + /* for ChipIO node */ + VENDOR_CHIPIO_ADDRESS_LOW = 0x000, + VENDOR_CHIPIO_ADDRESS_HIGH = 0x100, + VENDOR_CHIPIO_STREAM_FORMAT = 0x200, + VENDOR_CHIPIO_DATA_LOW = 0x300, + VENDOR_CHIPIO_DATA_HIGH = 0x400, + + VENDOR_CHIPIO_GET_PARAMETER = 0xF00, + VENDOR_CHIPIO_STATUS = 0xF01, + VENDOR_CHIPIO_HIC_POST_READ = 0x702, + VENDOR_CHIPIO_HIC_READ_DATA = 0xF03, + + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, + + VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C, + VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C, + VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D, + VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E, + VENDOR_CHIPIO_FLAG_SET = 0x70F, + VENDOR_CHIPIO_FLAGS_GET = 0xF0F, + VENDOR_CHIPIO_PARAMETER_SET = 0x710, + VENDOR_CHIPIO_PARAMETER_GET = 0xF10, + + VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711, + VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712, + VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12, + VENDOR_CHIPIO_PORT_FREE_SET = 0x713, + + VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17, + VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717, + VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18, + VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718 +}; + +/* + * Control flag IDs + */ +enum control_flag_id { + /* Connection manager stream setup is bypassed/enabled */ + CONTROL_FLAG_C_MGR = 0, + /* DSP DMA is bypassed/enabled */ + CONTROL_FLAG_DMA = 1, + /* 8051 'idle' mode is disabled/enabled */ + CONTROL_FLAG_IDLE_ENABLE = 2, + /* Tracker for the SPDIF-in path is bypassed/enabled */ + CONTROL_FLAG_TRACKER = 3, + /* DigitalOut to Spdif2Out connection is disabled/enabled */ + CONTROL_FLAG_SPDIF2OUT = 4, + /* Digital Microphone is disabled/enabled */ + CONTROL_FLAG_DMIC = 5, + /* ADC_B rate is 48 kHz/96 kHz */ + CONTROL_FLAG_ADC_B_96KHZ = 6, + /* ADC_C rate is 48 kHz/96 kHz */ + CONTROL_FLAG_ADC_C_96KHZ = 7, + /* DAC rate is 48 kHz/96 kHz (affects all DACs) */ + CONTROL_FLAG_DAC_96KHZ = 8, + /* DSP rate is 48 kHz/96 kHz */ + CONTROL_FLAG_DSP_96KHZ = 9, + /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */ + CONTROL_FLAG_SRC_CLOCK_196MHZ = 10, + /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */ + CONTROL_FLAG_SRC_RATE_96KHZ = 11, + /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */ + CONTROL_FLAG_DECODE_LOOP = 12, + /* De-emphasis filter on DAC-1 disabled/enabled */ + CONTROL_FLAG_DAC1_DEEMPHASIS = 13, + /* De-emphasis filter on DAC-2 disabled/enabled */ + CONTROL_FLAG_DAC2_DEEMPHASIS = 14, + /* De-emphasis filter on DAC-3 disabled/enabled */ + CONTROL_FLAG_DAC3_DEEMPHASIS = 15, + /* High-pass filter on ADC_B disabled/enabled */ + CONTROL_FLAG_ADC_B_HIGH_PASS = 16, + /* High-pass filter on ADC_C disabled/enabled */ + CONTROL_FLAG_ADC_C_HIGH_PASS = 17, + /* Common mode on Port_A disabled/enabled */ + CONTROL_FLAG_PORT_A_COMMON_MODE = 18, + /* Common mode on Port_D disabled/enabled */ + CONTROL_FLAG_PORT_D_COMMON_MODE = 19, + /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */ + CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20, + /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */ + CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21, + /* ASI rate is 48kHz/96kHz */ + CONTROL_FLAG_ASI_96KHZ = 22, + /* DAC power settings able to control attached ports no/yes */ + CONTROL_FLAG_DACS_CONTROL_PORTS = 23, + /* Clock Stop OK reporting is disabled/enabled */ + CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24, + /* Number of control flags */ + CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1) +}; + +/* + * Control parameter IDs + */ +enum control_parameter_id { + /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ + CONTROL_PARAM_SPDIF1_SOURCE = 2, + + /* Stream Control */ + + /* Select stream with the given ID */ + CONTROL_PARAM_STREAM_ID = 24, + /* Source connection point for the selected stream */ + CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25, + /* Destination connection point for the selected stream */ + CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26, + /* Number of audio channels in the selected stream */ + CONTROL_PARAM_STREAMS_CHANNELS = 27, + /*Enable control for the selected stream */ + CONTROL_PARAM_STREAM_CONTROL = 28, + + /* Connection Point Control */ + + /* Select connection point with the given ID */ + CONTROL_PARAM_CONN_POINT_ID = 29, + /* Connection point sample rate */ + CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30, + + /* Node Control */ + + /* Select HDA node with the given ID */ + CONTROL_PARAM_NODE_ID = 31 +}; + +/* + * Dsp Io Status codes + */ +enum hda_vendor_status_dspio { + /* Success */ + VENDOR_STATUS_DSPIO_OK = 0x00, + /* Busy, unable to accept new command, the host must retry */ + VENDOR_STATUS_DSPIO_BUSY = 0x01, + /* SCP command queue is full */ + VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02, + /* SCP response queue is empty */ + VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03 +}; + +/* + * Chip Io Status codes + */ +enum hda_vendor_status_chipio { + /* Success */ + VENDOR_STATUS_CHIPIO_OK = 0x00, + /* Busy, unable to accept new command, the host must retry */ + VENDOR_STATUS_CHIPIO_BUSY = 0x01 +}; + +/* + * CA0132 sample rate + */ +enum ca0132_sample_rate { + SR_6_000 = 0x00, + SR_8_000 = 0x01, + SR_9_600 = 0x02, + SR_11_025 = 0x03, + SR_16_000 = 0x04, + SR_22_050 = 0x05, + SR_24_000 = 0x06, + SR_32_000 = 0x07, + SR_44_100 = 0x08, + SR_48_000 = 0x09, + SR_88_200 = 0x0A, + SR_96_000 = 0x0B, + SR_144_000 = 0x0C, + SR_176_400 = 0x0D, + SR_192_000 = 0x0E, + SR_384_000 = 0x0F, + + SR_COUNT = 0x10, + + SR_RATE_UNKNOWN = 0x1F +}; + +/* + * Scp Helper function + */ +enum get_set { + IS_SET = 0, + IS_GET = 1, +}; + +/* + * Duplicated from ca0110 codec + */ + +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_VREF80); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc) + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); +} + +static char *dirstr[2] = { "Playback", "Capture" }; + +static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) +#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) +#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) +#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) +#define add_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 0) +#define add_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 0) +#define add_in_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 1) +#define add_in_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 1) + + +/* + * CA0132 specific + */ + +struct ca0132_spec { + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; + hda_nid_t hp_dac; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t adcs[AUTO_PIN_LAST]; + hda_nid_t dig_out; + hda_nid_t dig_in; + unsigned int num_inputs; + long curr_hp_switch; + long curr_hp_volume[2]; + long curr_speaker_switch; + struct mutex chipio_mutex; + const char *input_labels[AUTO_PIN_LAST]; + struct hda_pcm pcm_rec[2]; /* PCM information */ +}; + +/* Chip access helper function */ +static int chipio_send(struct hda_codec *codec, + unsigned int reg, + unsigned int data) +{ + unsigned int res; + int retry = 50; + + /* send bits of data specified by reg */ + do { + res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + reg, data); + if (res == VENDOR_STATUS_CHIPIO_OK) + return 0; + } while (--retry); + return -EIO; +} + +/* + * Write chip address through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_write_address(struct hda_codec *codec, + unsigned int chip_addx) +{ + int res; + + /* send low 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, + chip_addx & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, + chip_addx >> 16); + } + + return res; +} + +/* + * Write data through the vendor widget -- NOT protected by the Mutex! + */ + +static int chipio_write_data(struct hda_codec *codec, unsigned int data) +{ + int res; + + /* send low 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, + data >> 16); + } + + return res; +} + +/* + * Read data through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_read_data(struct hda_codec *codec, unsigned int *data) +{ + int res; + + /* post read */ + res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); + + if (res != -EIO) { + /* read status */ + res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + } + + if (res != -EIO) { + /* read data */ + *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_HIC_READ_DATA, + 0); + } + + return res; +} + +/* + * Write given value to the given address through the chip I/O widget. + * protected by the Mutex + */ +static int chipio_write(struct hda_codec *codec, + unsigned int chip_addx, const unsigned int data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_write_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +/* + * Read the given address through the chip I/O widget + * protected by the Mutex + */ +static int chipio_read(struct hda_codec *codec, + unsigned int chip_addx, unsigned int *data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_read_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +/* + * PCM stuffs + */ +static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, + int channel_id, int format) +{ + unsigned int oldval, newval; + + if (!nid) + return; + + snd_printdd("ca0132_setup_stream: " + "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", + nid, stream_tag, channel_id, format); + + /* update the format-id if changed */ + oldval = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_STREAM_FORMAT, + 0); + if (oldval != format) { + msleep(20); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, + format); + } + + oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + newval = (stream_tag << 4) | channel_id; + if (oldval != newval) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + newval); + } +} + +static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); +} + +/* + * PCM callbacks + */ +static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); + + return 0; +} + +static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dacs[0]); + + return 0; +} + +/* + * Digital out + */ +static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format); + + return 0; +} + +static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dig_out); + + return 0; +} + +/* + * Analog capture + */ +static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->adcs[substream->number], + stream_tag, 0, format); + + return 0; +} + +static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->adcs[substream->number]); + + return 0; +} + +/* + * Digital capture + */ +static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format); + + return 0; +} + +static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_cleanup_stream(codec, spec->dig_in); + + return 0; +} + +/* + */ +static struct hda_pcm_stream ca0132_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_playback_pcm_prepare, + .cleanup = ca0132_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_capture_pcm_prepare, + .cleanup = ca0132_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_dig_playback_pcm_prepare, + .cleanup = ca0132_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0132_dig_capture_pcm_prepare, + .cleanup = ca0132_dig_capture_pcm_cleanup + }, +}; + +static int ca0132_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0132 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0132 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +#define REG_CODEC_MUTE 0x18b014 +#define REG_CODEC_HP_VOL_L 0x18b070 +#define REG_CODEC_HP_VOL_R 0x18b074 + +static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp = spec->curr_hp_switch; + return 0; +} + +static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + unsigned int data; + int err; + + /* any change? */ + if (spec->curr_hp_switch == *valp) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_MUTE, &data); + if (err < 0) + return err; + + /* *valp 0 is mute, 1 is unmute */ + data = (data & 0x7f) | (*valp ? 0 : 0x80); + chipio_write(codec, REG_CODEC_MUTE, data); + if (err < 0) + return err; + + spec->curr_hp_switch = *valp; + + snd_hda_power_down(codec); + return 1; +} + +static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp = spec->curr_speaker_switch; + return 0; +} + +static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + unsigned int data; + int err; + + /* any change? */ + if (spec->curr_speaker_switch == *valp) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_MUTE, &data); + if (err < 0) + return err; + + /* *valp 0 is mute, 1 is unmute */ + data = (data & 0xef) | (*valp ? 0 : 0x10); + chipio_write(codec, REG_CODEC_MUTE, data); + if (err < 0) + return err; + + spec->curr_speaker_switch = *valp; + + snd_hda_power_down(codec); + return 1; +} + +static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp++ = spec->curr_hp_volume[0]; + *valp = spec->curr_hp_volume[1]; + return 0; +} + +static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + long left_vol, right_vol; + unsigned int data; + int val; + int err; + + left_vol = *valp++; + right_vol = *valp; + + /* any change? */ + if ((spec->curr_hp_volume[0] == left_vol) && + (spec->curr_hp_volume[1] == right_vol)) + return 0; + + snd_hda_power_up(codec); + + err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); + if (err < 0) + return err; + + val = 31 - left_vol; + data = (data & 0xe0) | val; + chipio_write(codec, REG_CODEC_HP_VOL_L, data); + if (err < 0) + return err; + + val = 31 - right_vol; + data = (data & 0xe0) | val; + chipio_write(codec, REG_CODEC_HP_VOL_R, data); + if (err < 0) + return err; + + spec->curr_hp_volume[0] = left_vol; + spec->curr_hp_volume[1] = right_vol; + + snd_hda_power_down(codec); + return 1; +} + +static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Headphone Playback Switch", + nid, 1, 0, HDA_OUTPUT); + knew.get = ca0132_hp_switch_get; + knew.put = ca0132_hp_switch_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO("Headphone Playback Volume", + nid, 3, 0, HDA_OUTPUT); + knew.get = ca0132_hp_volume_get; + knew.put = ca0132_hp_volume_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", + nid, 1, 0, HDA_OUTPUT); + knew.get = ca0132_speaker_switch_get; + knew.put = ca0132_speaker_switch_put; + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static void ca0132_fix_hp_caps(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int caps; + + /* set mute-capable, 1db step, 32 steps, ofs 6 */ + caps = 0x80031f06; + snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps); +} + +static int ca0132_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + if (spec->multiout.num_dacs) { + err = add_speaker_switch(codec, spec->out_pins[0]); + if (err < 0) + return err; + } + + if (cfg->hp_outs) { + ca0132_fix_hp_caps(codec); + err = add_hp_switch(codec, cfg->hp_pins[0]); + if (err < 0) + return err; + err = add_hp_volume(codec, cfg->hp_pins[0]); + if (err < 0) + return err; + } + + for (i = 0; i < spec->num_inputs; i++) { + const char *label = spec->input_labels[i]; + + err = add_in_switch(codec, spec->adcs[i], label); + if (err < 0) + return err; + err = add_in_volume(codec, spec->adcs[i], label); + if (err < 0) + return err; + if (cfg->inputs[i].type == AUTO_PIN_MIC) { + /* add Mic-Boost */ + err = add_in_mono_volume(codec, spec->input_pins[i], + "Mic Boost", 1); + if (err < 0) + return err; + } + } + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); + if (err < 0) + return err; + err = add_out_volume(codec, spec->dig_out, "IEC958"); + if (err < 0) + return err; + } + + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + err = add_in_volume(codec, spec->dig_in, "IEC958"); + } + return 0; +} + + +static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) +{ + /* Set Creative extension */ + snd_printdd("SET CREATIVE EXTENSION\n"); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, + enable); + msleep(20); +} + + +static void ca0132_config(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + /* line-outs */ + cfg->line_outs = 1; + cfg->line_out_pins[0] = 0x0b; /* front */ + cfg->line_out_type = AUTO_PIN_LINE_OUT; + + spec->dacs[0] = 0x02; + spec->out_pins[0] = 0x0b; + spec->multiout.dac_nids = spec->dacs; + spec->multiout.num_dacs = 1; + spec->multiout.max_channels = 2; + + /* headphone */ + cfg->hp_outs = 1; + cfg->hp_pins[0] = 0x0f; + + spec->hp_dac = 0; + spec->multiout.hp_nid = 0; + + /* inputs */ + cfg->num_inputs = 2; /* Mic-in and line-in */ + cfg->inputs[0].pin = 0x12; + cfg->inputs[0].type = AUTO_PIN_MIC; + cfg->inputs[1].pin = 0x11; + cfg->inputs[1].type = AUTO_PIN_LINE_IN; + + /* Mic-in */ + spec->input_pins[0] = 0x12; + spec->input_labels[0] = "Mic-In"; + spec->adcs[0] = 0x07; + + /* Line-In */ + spec->input_pins[1] = 0x11; + spec->input_labels[1] = "Line-In"; + spec->adcs[1] = 0x08; + spec->num_inputs = 2; +} + +static void ca0132_init_chip(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_init(&spec->chipio_mutex); +} + +static void ca0132_exit_chip(struct hda_codec *codec) +{ + /* put any chip cleanup stuffs here. */ +} + +static int ca0132_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + init_output(codec, spec->out_pins[i], + spec->multiout.dac_nids[i]); + } + init_output(codec, cfg->hp_pins[0], spec->hp_dac); + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + + init_input(codec, cfg->dig_in_pin, spec->dig_in); + + ca0132_set_ct_ext(codec, 1); + + return 0; +} + + +static void ca0132_free(struct hda_codec *codec) +{ + ca0132_set_ct_ext(codec, 0); + ca0132_exit_chip(codec); + kfree(codec->spec); +} + +static struct hda_codec_ops ca0132_patch_ops = { + .build_controls = ca0132_build_controls, + .build_pcms = ca0132_build_pcms, + .init = ca0132_init, + .free = ca0132_free, +}; + + + +static int patch_ca0132(struct hda_codec *codec) +{ + struct ca0132_spec *spec; + + snd_printdd("patch_ca0132\n"); + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + ca0132_init_chip(codec); + + ca0132_config(codec); + + codec->patch_ops = ca0132_patch_ops; + + return 0; +} + +/* + * patch entries + */ +static struct hda_codec_preset snd_hda_preset_ca0132[] = { + { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 }, + {} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:11020011"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec"); + +static struct hda_codec_preset_list ca0132_list = { + .preset = snd_hda_preset_ca0132, + .owner = THIS_MODULE, +}; + +static int __init patch_ca0132_init(void) +{ + return snd_hda_add_codec_preset(&ca0132_list); +} + +static void __exit patch_ca0132_exit(void) +{ + snd_hda_delete_codec_preset(&ca0132_list); +} + +module_init(patch_ca0132_init) +module_exit(patch_ca0132_exit) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 26a1521045bb..47d6ffc9b5b5 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -25,6 +25,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include <sound/tlv.h> /* */ @@ -61,9 +62,15 @@ struct cs_spec { unsigned int hp_detect:1; unsigned int mic_detect:1; + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + struct hda_input_mux input_mux; + unsigned int last_input; }; -/* available models */ +/* available models with CS420x */ enum { CS420X_MBP53, CS420X_MBP55, @@ -72,6 +79,12 @@ enum { CS420X_MODELS }; +/* CS421x boards */ +enum { + CS421X_CDB4210, + CS421X_MODELS +}; + /* Vendor-specific processing widget */ #define CS420X_VENDOR_NID 0x11 #define CS_DIG_OUT1_PIN_NID 0x10 @@ -111,21 +124,42 @@ enum { /* 0x0009 - 0x0014 -> 12 test regs */ /* 0x0015 - visibility reg */ +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Trasmitter(sense) +*/ +#define CS4210_DAC_NID 0x02 +#define CS4210_ADC_NID 0x03 +#define CS421X_VENDOR_NID 0x0B +#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ +#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ + +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 + +#define SPDIF_EVENT 0x04 static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) { - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0, + return snd_hda_codec_read(codec, spec->vendor_nid, 0, AC_VERB_GET_PROC_COEF, 0); } static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, unsigned int coef) { - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_PROC_COEF, coef); } @@ -346,22 +380,13 @@ static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t pins[2]; unsigned int type; - int j, nums; type = get_wcaps_type(get_wcaps(codec, nid)); if (type != AC_WID_AUD_IN) continue; - nums = snd_hda_get_connections(codec, nid, pins, - ARRAY_SIZE(pins)); - if (nums <= 0) - continue; - for (j = 0; j < nums; j++) { - if (pins[j] == pin) { - *idxp = j; - return nid; - } - } + *idxp = snd_hda_get_conn_index(codec, nid, pin, false); + if (*idxp >= 0) + return nid; } return 0; } @@ -821,7 +846,8 @@ static int build_digital_output(struct hda_codec *codec) if (!spec->multiout.dig_out_nid) return 0; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); @@ -840,6 +866,8 @@ static int build_digital_input(struct hda_codec *codec) /* * auto-mute and auto-mic switching + * CS421x auto-output redirecting + * HP/SPK/SPDIF */ static void cs_automute(struct hda_codec *codec) @@ -847,9 +875,25 @@ static void cs_automute(struct hda_codec *codec) struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int hp_present; + unsigned int spdif_present; hda_nid_t nid; int i; + spdif_present = 0; + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + if (is_jack_detectable(codec, nid)) { + /* + TODO: SPDIF output redirect when SENSE_B is enabled. + Shared (SENSE_A) jack (e.g HP/mini-TOSLINK) + assumed. + */ + if (snd_hda_jack_detect(codec, nid) + /* && spec->sense_b */) + spdif_present = 1; + } + } + hp_present = 0; for (i = 0; i < cfg->hp_outs; i++) { nid = cfg->hp_pins[i]; @@ -859,11 +903,19 @@ static void cs_automute(struct hda_codec *codec) if (hp_present) break; } + + /* mute speakers if spdif or hp jack is plugged in */ for (i = 0; i < cfg->speaker_outs; i++) { nid = cfg->speaker_pins[i]; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, hp_present ? 0 : PIN_OUT); + /* detect on spdif is specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spdif_present ? 0 : PIN_OUT); + } } if (spec->board_config == CS420X_MBP53 || spec->board_config == CS420X_MBP55 || @@ -872,21 +924,62 @@ static void cs_automute(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); } + + /* specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + /* mute HPs if spdif jack (SENSE_B) is present */ + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + (spdif_present && spec->sense_b) ? 0 : PIN_HP); + } + + /* SPDIF TX on/off */ + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spdif_present ? PIN_OUT : 0); + + } + /* Update board GPIOs if neccessary ... */ + } } +/* + * Auto-input redirect for CS421x + * Switch max 3 inputs of a single ADC (nid 3) +*/ + static void cs_automic(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; unsigned int present; - + nid = cfg->inputs[spec->automic_idx].pin; present = snd_hda_jack_detect(codec, nid); - if (present) - change_cur_input(codec, spec->automic_idx, 0); - else - change_cur_input(codec, !spec->automic_idx, 0); + + /* specific to CS421x, single ADC */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + if (present) { + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; + } else { + spec->cur_input = spec->last_input; + } + + snd_hda_codec_write_cache(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); + } else { + if (present) + change_cur_input(codec, spec->automic_idx, 0); + else + change_cur_input(codec, !spec->automic_idx, 0); + } } /* @@ -916,23 +1009,28 @@ static void init_output(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* HP */ for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); if (!cfg->speaker_outs) continue; - if (is_jack_detectable(codec, nid)) { + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | HP_EVENT); spec->hp_detect = 1; } } + + /* Speaker */ for (i = 0; i < cfg->speaker_outs; i++) snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (spec->hp_detect) + + /* SPDIF is enabled on presence detect for CS421x */ + if (spec->hp_detect || spec->spdif_detect) cs_automute(codec); } @@ -966,19 +1064,31 @@ static void init_input(struct hda_codec *codec) AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | MIC_EVENT); } - change_cur_input(codec, spec->cur_input, 1); - if (spec->mic_detect) - cs_automic(codec); - - coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ - if (is_active_pin(codec, CS_DMIC2_PIN_NID)) - coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */ - if (is_active_pin(codec, CS_DMIC1_PIN_NID)) - coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 - * No effect if SPDIF_OUT2 is selected in - * IDX_SPDIF_CTL. - */ - cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); + /* specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + if (spec->mic_detect) + cs_automic(codec); + else { + spec->cur_adc = spec->adc_nid[spec->cur_input]; + snd_hda_codec_write(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); + } + } else { + change_cur_input(codec, spec->cur_input, 1); + if (spec->mic_detect) + cs_automic(codec); + + coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ + if (is_active_pin(codec, CS_DMIC2_PIN_NID)) + coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */ + if (is_active_pin(codec, CS_DMIC1_PIN_NID)) + coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off + * No effect if SPDIF_OUT2 is + * selected in IDX_SPDIF_CTL. + */ + cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); + } } static const struct hda_verb cs_coef_init_verbs[] = { @@ -1226,16 +1336,16 @@ static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { [CS420X_IMAC27] = imac27_pincfgs, }; -static void fix_pincfg(struct hda_codec *codec, int model) +static void fix_pincfg(struct hda_codec *codec, int model, + const struct cs_pincfg **pin_configs) { - const struct cs_pincfg *cfg = cs_pincfgs[model]; + const struct cs_pincfg *cfg = pin_configs[model]; if (!cfg) return; for (; cfg->nid; cfg++) snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); } - static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; @@ -1246,11 +1356,13 @@ static int patch_cs420x(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + spec->vendor_nid = CS420X_VENDOR_NID; + spec->board_config = snd_hda_check_board_config(codec, CS420X_MODELS, cs420x_models, cs420x_cfg_tbl); if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config); + fix_pincfg(codec, spec->board_config, cs_pincfgs); switch (spec->board_config) { case CS420X_IMAC27: @@ -1277,6 +1389,562 @@ static int patch_cs420x(struct hda_codec *codec) return err; } +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Trasmitter(sense) +*/ + +/* CS4210 board names */ +static const char *cs421x_models[CS421X_MODELS] = { + [CS421X_CDB4210] = "cdb4210", +}; + +static const struct snd_pci_quirk cs421x_cfg_tbl[] = { + /* Test Intel board + CDB2410 */ + SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), + {} /* terminator */ +}; + +/* CS4210 board pinconfigs */ +/* Default CS4210 (CDB4210)*/ +static const struct cs_pincfg cdb4210_pincfgs[] = { + { 0x05, 0x0321401f }, + { 0x06, 0x90170010 }, + { 0x07, 0x03813031 }, + { 0x08, 0xb7a70037 }, + { 0x09, 0xb7a6003e }, + { 0x0a, 0x034510f0 }, + {} /* terminator */ +}; + +static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = { + [CS421X_CDB4210] = cdb4210_pincfgs, +}; + +static const struct hda_verb cs421x_coef_init_verbs[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 1}, + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, + /* + Disable Coefficient Index Auto-Increment(DAI)=1, + PDREF=0 + */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, + /* ADC SZCMode = Digital Soft Ramp */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, + {0x0B, AC_VERB_SET_PROC_COEF, + (0x0002 /* DAC SZCMode = Digital Soft Ramp */ + | 0x0004 /* Mute DAC on FIFO error */ + | 0x0008 /* Enable DAC High Pass Filter */ + )}, + {} /* terminator */ +}; + +/* Errata: CS4210 rev A1 Silicon + * + * http://www.cirrus.com/en/pubs/errata/ + * + * Description: + * 1. Performance degredation is present in the ADC. + * 2. Speaker output is not completely muted upon HP detect. + * 3. Noise is present when clipping occurs on the amplified + * speaker outputs. + * + * Workaround: + * The following verb sequence written to the registers during + * initialization will correct the issues listed above. + */ + +static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, + {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, + {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ + + {} /* terminator */ +}; + +/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ +static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); + +static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; + return 0; +} + +static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + unsigned int vol = ucontrol->value.integer.value[0]; + unsigned int coef = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); + unsigned int original_coef = coef; + + coef &= ~0x0003; + coef |= (vol & 0x0003); + if (original_coef == coef) + return 0; + else { + cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); + return 1; + } +} + +static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Speaker Boost Playback Volume", + .info = cs421x_boost_vol_info, + .get = cs421x_boost_vol_get, + .put = cs421x_boost_vol_put, + .tlv = { .p = cs421x_speaker_boost_db_scale }, +}; + +static void cs421x_pinmux_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int def_conf, coef; + + /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + + if (spec->gpio_mask) + coef |= 0x0008; /* B1,B2 are GPIOs */ + else + coef &= ~0x0008; + + if (spec->sense_b) + coef |= 0x0010; /* B2 is SENSE_B, not inverted */ + else + coef &= ~0x0010; + + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + if ((spec->gpio_mask || spec->sense_b) && + is_active_pin(codec, CS421X_DMIC_PIN_NID)) { + + /* + GPIO or SENSE_B forced - disconnect the DMIC pin. + */ + def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); + def_conf &= ~AC_DEFCFG_PORT_CONN; + def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); + snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); + } +} + +static void init_cs421x_digital(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t nid = cfg->dig_out_pins[i]; + if (!cfg->speaker_outs) + continue; + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | SPDIF_EVENT); + spec->spdif_detect = 1; + } + } +} + +static int cs421x_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_sequence_write(codec, cs421x_coef_init_verbs); + snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); + + cs421x_pinmux_init(codec); + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + init_output(codec); + init_input(codec); + init_cs421x_digital(codec); + + return 0; +} + +/* + * CS4210 Input MUX (1 ADC) + */ +static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_input; + return 0; +} + +static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->adc_nid[0], &spec->cur_input); + +} + +static struct snd_kcontrol_new cs421x_capture_source = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs421x_mux_enum_info, + .get = cs421x_mux_enum_get, + .put = cs421x_mux_enum_put, +}; + +static int cs421x_add_input_volume_control(struct hda_codec *codec, int item) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + const struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t pin = cfg->inputs[item].pin; + struct snd_kcontrol *kctl; + u32 caps; + + if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) + return 0; + + caps = query_amp_caps(codec, pin, HDA_INPUT); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (caps <= 1) + return 0; + + return add_volume(codec, imux->items[item].label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + +/* add a (input-boost) volume control to the given input pin */ +static int build_cs421x_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type_idx; + const char *label; + + if (!spec->num_inputs) + return 0; + + /* make bind-capture */ + spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); + spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + int n; + if (!spec->capture_bind[i]) + return -ENOMEM; + kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = (long)spec->capture_bind[i]; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + for (n = 0; n < AUTO_PIN_LAST; n++) { + if (!spec->adc_nid[n]) + continue; + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); + if (err < 0) + return err; + } + } + + /* Add Input MUX Items + Capture Volume/Switch */ + for (i = 0; i < spec->num_inputs; i++) { + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx); + + err = cs421x_add_input_volume_control(codec, i); + if (err < 0) + return err; + } + + /* + Add 'Capture Source' Switch if + * 2 inputs and no mic detec + * 3 inputs + */ + if ((spec->num_inputs == 2 && !spec->mic_detect) || + (spec->num_inputs == 3)) { + + err = snd_hda_ctl_add(codec, spec->adc_nid[0], + snd_ctl_new1(&cs421x_capture_source, codec)); + if (err < 0) + return err; + } + + return 0; +} + +/* Single DAC (Mute/Gain) */ +static int build_cs421x_output(struct hda_codec *codec) +{ + hda_nid_t dac = CS4210_DAC_NID; + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct snd_kcontrol *kctl; + int err; + char *name = "HP/Speakers"; + + fix_volume_caps(codec, dac); + if (!spec->vmaster_sw) { + err = add_vmaster(codec, dac); + if (err < 0) + return err; + } + + err = add_mute(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_sw, kctl); + if (err < 0) + return err; + + err = add_volume(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_vol, kctl); + if (err < 0) + return err; + + if (cfg->speaker_outs) { + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); + if (err < 0) + return err; + } + return err; +} + +static int cs421x_build_controls(struct hda_codec *codec) +{ + int err; + + err = build_cs421x_output(codec); + if (err < 0) + return err; + err = build_cs421x_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + return cs421x_init(codec); +} + +static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) +{ + switch ((res >> 26) & 0x3f) { + case HP_EVENT: + case SPDIF_EVENT: + cs_automute(codec); + break; + + case MIC_EVENT: + cs_automic(codec); + break; + } +} + +static int parse_cs421x_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + spec->cur_input = spec->last_input = i; + spec->num_inputs++; + + /* check whether the automatic mic switch is available */ + if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) { + spec->mic_detect = 1; + spec->automic_idx = i; + } + } + return 0; +} + +static int cs421x_parse_auto_config(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = parse_output(codec); + if (err < 0) + return err; + err = parse_cs421x_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); + if (err < 0) + return err; + return 0; +} + +#ifdef CONFIG_PM +/* + Manage PDREF, when transitioning to D3hot + (DAC,ADC) -> D3, PDREF=1, AFG->D3 +*/ +static int cs421x_suspend(struct hda_codec *codec, pm_message_t state) +{ + unsigned int coef; + + snd_hda_shutup_pins(codec); + + snd_hda_codec_write(codec, CS4210_DAC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, CS4210_ADC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + coef |= 0x0004; /* PDREF */ + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + return 0; +} +#endif + +static struct hda_codec_ops cs4210_patch_ops = { + .build_controls = cs421x_build_controls, + .build_pcms = cs_build_pcms, + .init = cs421x_init, + .free = cs_free, + .unsol_event = cs421x_unsol_event, +#ifdef CONFIG_PM + .suspend = cs421x_suspend, +#endif +}; + +static int patch_cs421x(struct hda_codec *codec) +{ + struct cs_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + spec->vendor_nid = CS421X_VENDOR_NID; + + spec->board_config = + snd_hda_check_board_config(codec, CS421X_MODELS, + cs421x_models, cs421x_cfg_tbl); + if (spec->board_config >= 0) + fix_pincfg(codec, spec->board_config, cs421x_pincfgs); + /* + Setup GPIO/SENSE for each board (if used) + */ + switch (spec->board_config) { + case CS421X_CDB4210: + snd_printd("CS4210 board: %s\n", + cs421x_models[spec->board_config]); +/* spec->gpio_mask = 3; + spec->gpio_dir = 3; + spec->gpio_data = 3; +*/ + spec->sense_b = 1; + + break; + } + + /* + Update the GPIO/DMIC/SENSE_B pinmux before the configuration + is auto-parsed. If GPIO or SENSE_B is forced, DMIC input + is disabled. + */ + cs421x_pinmux_init(codec); + + err = cs421x_parse_auto_config(codec); + if (err < 0) + goto error; + + codec->patch_ops = cs4210_patch_ops; + + return 0; + + error: + kfree(codec->spec); + codec->spec = NULL; + return err; +} + /* * patch entries @@ -1284,11 +1952,13 @@ static int patch_cs420x(struct hda_codec *codec) static const struct hda_codec_preset snd_hda_preset_cirrus[] = { { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, + { .id = 0x10134210, .name = "CS4210", .patch = patch_cs421x }, {} /* terminator */ }; MODULE_ALIAS("snd-hda-codec-id:10134206"); MODULE_ALIAS("snd-hda-codec-id:10134207"); +MODULE_ALIAS("snd-hda-codec-id:10134210"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index ab3308daa960..cd2cf5e94e81 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -327,7 +327,9 @@ static int cmi9880_build_controls(struct hda_codec *codec) return err; } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, @@ -396,12 +398,11 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi { struct cmi_spec *spec = codec->spec; hda_nid_t nid; - int i, j, k, len; + int i, j, k; /* clear the table, only one c-media dac assumed here */ memset(spec->multi_init, 0, sizeof(spec->multi_init)); for (j = 0, i = 0; i < cfg->line_outs; i++) { - hda_nid_t conn[4]; nid = cfg->line_out_pins[i]; /* set as output */ spec->multi_init[j].nid = nid; @@ -414,12 +415,10 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; spec->multi_init[j].param = 0; /* find the index in connect list */ - len = snd_hda_get_connections(codec, nid, conn, 4); - for (k = 0; k < len; k++) - if (conn[k] == spec->dac_nids[i]) { - spec->multi_init[j].param = k; - break; - } + k = snd_hda_get_conn_index(codec, nid, + spec->dac_nids[i], 0); + if (k >= 0) + spec->multi_init[j].param = k; j++; } } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7bbc5f237a5e..502fc9499453 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -155,6 +155,10 @@ struct conexant_spec { unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ unsigned int beep_amp; + + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -442,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec) return 0; } +static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + power_state); + /* partial workaround for "azx_get_response timeout" */ + if (power_state == AC_PWRST_D0) + msleep(10); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} + static int conexant_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -510,6 +527,7 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; @@ -583,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = { .build_pcms = conexant_build_pcms, .init = conexant_init, .free = conexant_free, + .set_power_state = conexant_set_power, #ifdef CONFIG_SND_HDA_POWER_SAVE .suspend = conexant_suspend, #endif @@ -1123,10 +1142,8 @@ static int patch_cxt5045(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, cxt5045_models, cxt5045_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5045_AUTO; -#endif + board_config = CXT5045_AUTO; /* model=auto as default */ if (board_config == CXT5045_AUTO) return patch_conexant_auto(codec); @@ -1564,10 +1581,8 @@ static int patch_cxt5047(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5047_MODELS, cxt5047_models, cxt5047_cfg_tbl); -#if 0 /* not enabled as default, as BIOS often broken for this codec */ if (board_config < 0) - board_config = CXT5047_AUTO; -#endif + board_config = CXT5047_AUTO; /* model=auto as default */ if (board_config == CXT5047_AUTO) return patch_conexant_auto(codec); @@ -1993,10 +2008,8 @@ static int patch_cxt5051(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, cxt5051_models, cxt5051_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5051_AUTO; -#endif + board_config = CXT5051_AUTO; /* model=auto as default */ if (board_config == CXT5051_AUTO) return patch_conexant_auto(codec); @@ -3114,10 +3127,8 @@ static int patch_cxt5066(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, cxt5066_models, cxt5066_cfg_tbl); -#if 0 /* use the old method just for safety */ if (board_config < 0) - board_config = CXT5066_AUTO; -#endif + board_config = CXT5066_AUTO; /* model=auto as default */ if (board_config == CXT5066_AUTO) return patch_conexant_auto(codec); @@ -3308,19 +3319,8 @@ static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; -/* get the connection index of @nid in the widget @mux */ -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} +#define get_connection_index(codec, mux, nid)\ + snd_hda_get_conn_index(codec, mux, nid, 0) /* get an unassigned DAC from the given list. * Return the nid if found and reduce the DAC list, or return zero if @@ -3919,6 +3919,38 @@ static void cx_auto_parse_beep(struct hda_codec *codec) #define cx_auto_parse_beep(codec) #endif +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; +} + +/* parse extra-EAPD that aren't assigned to any pins */ +static void cx_auto_parse_eapd(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + continue; + 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)) + continue; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + break; + } +} + static int cx_auto_parse_auto_config(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -3932,6 +3964,7 @@ static int cx_auto_parse_auto_config(struct hda_codec *codec) cx_auto_parse_input(codec); cx_auto_parse_digital(codec); cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); return 0; } @@ -4019,6 +4052,8 @@ static void cx_auto_init_output(struct hda_codec *codec) } } cx_auto_update_speakers(codec); + /* turn on/off extra EAPDs, too */ + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); } static void cx_auto_init_input(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bd0ae697f9c4..19cb72db9c38 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); /* * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device - * could support two independent pipes, each of them can be connected to one or + * could support N independent pipes, each of them can be connected to one or * more ports (DVI, HDMI or DisplayPort). * * The HDA correspondence of pipes/ports are converter/pin nodes. @@ -51,30 +51,33 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define MAX_HDMI_CVTS 4 #define MAX_HDMI_PINS 4 -struct hdmi_spec { - int num_cvts; - int num_pins; - hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */ - hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */ +struct hdmi_spec_per_cvt { + hda_nid_t cvt_nid; + int assigned; + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; - /* - * source connection for each pin - */ - hda_nid_t pin_cvt[MAX_HDMI_PINS+1]; +struct hdmi_spec_per_pin { + hda_nid_t pin_nid; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + struct hdmi_eld sink_eld; +}; - /* - * HDMI sink attached to each pin - */ - struct hdmi_eld sink_eld[MAX_HDMI_PINS]; +struct hdmi_spec { + int num_cvts; + struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; - /* - * export one pcm per pipe - */ - struct hda_pcm pcm_rec[MAX_HDMI_CVTS]; - struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS]; + int num_pins; + struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; + struct hda_pcm pcm_rec[MAX_HDMI_PINS]; /* - * ati/nvhdmi specific + * Non-generic ATI/NVIDIA specific */ struct hda_multi_out multiout; const struct hda_pcm_stream *pcm_playback; @@ -284,15 +287,40 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { * HDMI routines */ -static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid) { - int i; + int pin_idx; - for (i = 0; nids[i]; i++) - if (nids[i] == nid) - return i; + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) + if (spec->pins[pin_idx].pin_nid == pin_nid) + return pin_idx; - snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid); + return -EINVAL; +} + +static int hinfo_to_pin_index(struct hdmi_spec *spec, + struct hda_pcm_stream *hinfo) +{ + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) + if (&spec->pcm_rec[pin_idx].stream[0] == hinfo) + return pin_idx; + + snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo); + return -EINVAL; +} + +static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid) +{ + int cvt_idx; + + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) + if (spec->cvts[cvt_idx].cvt_nid == cvt_nid) + return cvt_idx; + + snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid); return -EINVAL; } @@ -326,28 +354,28 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); } -static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) +static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Enable pin out */ + /* Disable pin out until stream is active*/ snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) { - return 1 + snd_hda_codec_read(codec, nid, 0, + return 1 + snd_hda_codec_read(codec, cvt_nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t nid, int chs) + hda_nid_t cvt_nid, int chs) { - if (chs != hdmi_get_channel_count(codec, nid)) - snd_hda_codec_write(codec, nid, 0, + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } @@ -384,11 +412,8 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, - int channels) +static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) { - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; int i; int ca = 0; int spk_mask = 0; @@ -400,19 +425,6 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, if (channels <= 2) return 0; - i = hda_node_index(spec->pin_cvt, nid); - if (i < 0) - return 0; - eld = &spec->sink_eld[i]; - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!eld->spk_alloc) - eld->spk_alloc = 0xffff; - /* * expand ELD's speaker allocation mask * @@ -608,67 +620,63 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - hda_nid_t pin_nid; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; int channels = substream->runtime->channels; + struct hdmi_eld *eld; int ca; - int i; union audio_infoframe ai; - ca = hdmi_channel_allocation(codec, nid, channels); - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!spec->sink_eld[i].monitor_present) - continue; + eld = &spec->pins[pin_idx].sink_eld; + if (!eld->monitor_present) + return; - pin_nid = spec->pin[i]; - - memset(&ai, 0, sizeof(ai)); - if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = channels - 1; - dp_ai->CA = ca; - } else { - snd_printd("HDMI: unknown connection type at pin %d\n", - pin_nid); - continue; - } + ca = hdmi_channel_allocation(eld, channels); + + memset(&ai, 0, sizeof(ai)); + if (eld->conn_type == 0) { /* HDMI */ + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + hdmi_ai->CC02_CT47 = channels - 1; + hdmi_ai->CA = ca; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (eld->conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai = &ai.dp; + + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = channels - 1; + dp_ai->CA = ca; + } else { + snd_printd("HDMI: unknown connection type at pin %d\n", + pin_nid); + return; + } - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - snd_printdd("hdmi_setup_audio_infoframe: " - "cvt=%d pin=%d channels=%d\n", - nid, pin_nid, - channels); - hdmi_setup_channel_mapping(codec, pin_nid, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { + snd_printdd("hdmi_setup_audio_infoframe: " + "pin=%d channels=%d\n", + pin_nid, + channels); + hdmi_setup_channel_mapping(codec, pin_nid, ca); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai.bytes, sizeof(ai)); + hdmi_start_infoframe_trans(codec, pin_nid); } } @@ -686,17 +694,27 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT; int pd = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); - int index; + int pin_idx; + struct hdmi_eld *eld; printk(KERN_INFO - "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - pin_nid, pd, eldv); + "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, pd, eldv); - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) + pin_idx = pin_nid_to_pin_index(spec, pin_nid); + if (pin_idx < 0) return; + eld = &spec->pins[pin_idx].sink_eld; - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]); + hdmi_present_sense(codec, pin_nid, eld); + + /* + * HDMI sink's ELD info cannot always be retrieved for now, e.g. + * in console or for audio devices. Assume the highest speakers + * configuration, to _not_ prohibit multi-channel audio playback. + */ + if (!eld->spk_alloc) + eld->spk_alloc = 0xffff; } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -707,7 +725,8 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); printk(KERN_INFO - "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + codec->addr, tag, subtag, cp_state, @@ -727,7 +746,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (hda_node_index(spec->pin, tag) < 0) { + if (pin_nid_to_pin_index(spec, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -746,21 +765,14 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) #define is_hbr_format(format) \ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, int format) +static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) { - struct hdmi_spec *spec = codec->spec; int pinctl; int new_pinctl = 0; - int i; - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR)) - continue; - pinctl = snd_hda_codec_read(codec, spec->pin[i], 0, + if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { + pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_pinctl = pinctl & ~AC_PINCTL_EPT; @@ -771,22 +783,22 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, snd_printdd("hdmi_setup_stream: " "NID=0x%x, %spinctl=0x%x\n", - spec->pin[i], + pin_nid, pinctl == new_pinctl ? "" : "new-", new_pinctl); if (pinctl != new_pinctl) - snd_hda_codec_write(codec, spec->pin[i], 0, + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_pinctl); - } + } if (is_hbr_format(format) && !new_pinctl) { snd_printdd("hdmi_setup_stream: HBR is not supported\n"); return -EINVAL; } - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); + snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); return 0; } @@ -798,37 +810,70 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; - struct hda_pcm_stream *codec_pars; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int idx; + int pin_idx, cvt_idx, mux_idx = 0; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int pinctl; - for (idx = 0; idx < spec->num_cvts; idx++) - if (hinfo->nid == spec->cvt[idx]) - break; - if (snd_BUG_ON(idx >= spec->num_cvts) || - snd_BUG_ON(idx >= spec->num_pins)) + /* Validate hinfo */ + pin_idx = hinfo_to_pin_index(spec, hinfo); + if (snd_BUG_ON(pin_idx < 0)) return -EINVAL; + per_pin = &spec->pins[pin_idx]; + eld = &per_pin->sink_eld; - /* save the PCM info the codec provides */ - codec_pars = &spec->codec_pcm_pars[idx]; - if (!codec_pars->rates) - *codec_pars = *hinfo; + /* Dynamically assign converter to stream */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = &spec->cvts[cvt_idx]; - eld = &spec->sink_eld[idx]; - if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) { - hdmi_eld_update_pcm_info(eld, hinfo, codec_pars); + /* Must not already be assigned */ + if (per_cvt->assigned) + continue; + /* Must be in pin's mux's list of converters */ + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) + break; + /* Not in mux list */ + if (mux_idx == per_pin->num_mux_nids) + continue; + break; + } + /* No free converters */ + if (cvt_idx == spec->num_cvts) + return -ENODEV; + + /* Claim converter */ + per_cvt->assigned = 1; + hinfo->nid = per_cvt->cvt_nid; + + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl | PIN_OUT); + snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); + + /* Initially set the converter's capabilities */ + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; + + /* Restrict capabilities by ELD if this isn't disabled */ + if (!static_hdmi_pcm && eld->eld_valid) { + snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || !hinfo->rates || !hinfo->formats) return -ENODEV; - } else { - /* fallback to the codec default */ - hinfo->channels_max = codec_pars->channels_max; - hinfo->rates = codec_pars->rates; - hinfo->formats = codec_pars->formats; - hinfo->maxbps = codec_pars->maxbps; } - /* store the updated parameters */ + + /* Store the updated parameters */ runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; runtime->hw.formats = hinfo->formats; @@ -842,12 +887,11 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* * HDA/HDMI auto parsing */ -static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) { struct hdmi_spec *spec = codec->spec; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - int conn_len, curr; - int index; + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { snd_printk(KERN_WARNING @@ -857,19 +901,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } - conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, - HDA_MAX_CONNECTIONS); - if (conn_len > 1) - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - else - curr = 0; - - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) - return -EINVAL; - - spec->pin_cvt[index] = conn_list[curr]; + per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid, + per_pin->mux_nids, + HDA_MAX_CONNECTIONS); return 0; } @@ -896,8 +930,8 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, eld->eld_valid = 0; printk(KERN_INFO - "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - pin_nid, eld->monitor_present, eld->eld_valid); + "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); if (eld->eld_valid) if (!snd_hdmi_get_eld(eld, codec, pin_nid)) @@ -909,47 +943,75 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; + unsigned int caps, config; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + struct hdmi_eld *eld; int err; - if (spec->num_pins >= MAX_HDMI_PINS) { - snd_printk(KERN_WARNING - "HDMI: no space for pin %d\n", pin_nid); + caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP); + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) + return 0; + + config = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) + return 0; + + if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) return -E2BIG; - } + + pin_idx = spec->num_pins; + per_pin = &spec->pins[pin_idx]; + eld = &per_pin->sink_eld; + + per_pin->pin_nid = pin_nid; err = snd_hda_input_jack_add(codec, pin_nid, SND_JACK_VIDEOOUT, NULL); if (err < 0) return err; - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + err = hdmi_read_pin_conn(codec, pin_idx); + if (err < 0) + return err; - spec->pin[spec->num_pins] = pin_nid; spec->num_pins++; - return hdmi_read_pin_conn(codec, pin_nid); + hdmi_present_sense(codec, pin_nid, eld); + + return 0; } -static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) { - int i, found_pin = 0; struct hdmi_spec *spec = codec->spec; - - for (i = 0; i < spec->num_pins; i++) - if (nid == spec->pin_cvt[i]) { - found_pin = 1; - break; - } - - if (!found_pin) { - snd_printdd("HDMI: Skipping node %d (no connection)\n", nid); - return -EINVAL; - } + int cvt_idx; + struct hdmi_spec_per_cvt *per_cvt; + unsigned int chans; + int err; if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS)) return -E2BIG; - spec->cvt[spec->num_cvts] = nid; + chans = get_wcaps(codec, cvt_nid); + chans = get_wcaps_channels(chans); + + cvt_idx = spec->num_cvts; + per_cvt = &spec->cvts[cvt_idx]; + + per_cvt->cvt_nid = cvt_nid; + per_cvt->channels_min = 2; + if (chans <= 16) + per_cvt->channels_max = chans; + + err = snd_hda_query_supported_pcm(codec, cvt_nid, + &per_cvt->rates, + &per_cvt->formats, + &per_cvt->maxbps); + if (err < 0) + return err; + spec->num_cvts++; return 0; @@ -959,8 +1021,6 @@ static int hdmi_parse_codec(struct hda_codec *codec) { hda_nid_t nid; int i, nodes; - int num_tmp_cvts = 0; - hda_nid_t tmp_cvt[MAX_HDMI_CVTS]; nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); if (!nid || nodes < 0) { @@ -971,7 +1031,6 @@ static int hdmi_parse_codec(struct hda_codec *codec) for (i = 0; i < nodes; i++, nid++) { unsigned int caps; unsigned int type; - unsigned int config; caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); type = get_wcaps_type(caps); @@ -981,32 +1040,14 @@ static int hdmi_parse_codec(struct hda_codec *codec) switch (type) { case AC_WID_AUD_OUT: - if (num_tmp_cvts >= MAX_HDMI_CVTS) { - snd_printk(KERN_WARNING - "HDMI: no space for converter %d\n", nid); - continue; - } - tmp_cvt[num_tmp_cvts] = nid; - num_tmp_cvts++; + hdmi_add_cvt(codec, nid); break; case AC_WID_PIN: - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - continue; - - config = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) - continue; - hdmi_add_pin(codec, nid); break; } } - for (i = 0; i < num_tmp_cvts; i++) - hdmi_add_cvt(codec, tmp_cvt[i]); - /* * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event * can be lost and presence sense verb will become inaccurate if the @@ -1023,7 +1064,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) /* */ -static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = { +static char *generic_hdmi_pcm_names[MAX_HDMI_PINS] = { "HDMI 0", "HDMI 1", "HDMI 2", @@ -1040,51 +1081,84 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, hinfo->nid, - substream->runtime->channels); + hda_nid_t cvt_nid = hinfo->nid; + struct hdmi_spec *spec = codec->spec; + int pin_idx = hinfo_to_pin_index(spec, hinfo); + hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid; + + hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); + hdmi_setup_audio_infoframe(codec, pin_idx, substream); - return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } -static const struct hda_pcm_stream generic_hdmi_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .ops = { - .open = hdmi_pcm_open, - .prepare = generic_hdmi_playback_pcm_prepare, - }, +static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int cvt_idx, pin_idx; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + int pinctl; + + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + + if (hinfo->nid) { + cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid); + if (snd_BUG_ON(cvt_idx < 0)) + return -EINVAL; + per_cvt = &spec->cvts[cvt_idx]; + + snd_BUG_ON(!per_cvt->assigned); + per_cvt->assigned = 0; + hinfo->nid = 0; + + pin_idx = hinfo_to_pin_index(spec, hinfo); + if (snd_BUG_ON(pin_idx < 0)) + return -EINVAL; + per_pin = &spec->pins[pin_idx]; + + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl & ~PIN_OUT); + snd_hda_spdif_ctls_unassign(codec, pin_idx); + } + + return 0; +} + +static const struct hda_pcm_ops generic_ops = { + .open = hdmi_pcm_open, + .prepare = generic_hdmi_playback_pcm_prepare, + .cleanup = generic_hdmi_playback_pcm_cleanup, }; static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - int i; + int pin_idx; - codec->num_pcms = spec->num_cvts; - codec->pcm_info = info; - - for (i = 0; i < codec->num_pcms; i++, info++) { - unsigned int chans; + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hda_pcm *info; struct hda_pcm_stream *pstr; - chans = get_wcaps(codec, spec->cvt[i]); - chans = get_wcaps_channels(chans); - - info->name = generic_hdmi_pcm_names[i]; + info = &spec->pcm_rec[pin_idx]; + info->name = generic_hdmi_pcm_names[pin_idx]; info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - if (spec->pcm_playback) - *pstr = *spec->pcm_playback; - else - *pstr = generic_hdmi_pcm_playback; - pstr->nid = spec->cvt[i]; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; + pstr->substreams = 1; + pstr->ops = generic_ops; + /* other pstr fields are set in open */ } + codec->num_pcms = spec->num_pins; + codec->pcm_info = spec->pcm_rec; + return 0; } @@ -1092,12 +1166,16 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err; - int i; + int pin_idx; - for (i = 0; i < codec->num_pcms; i++) { - err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + err = snd_hda_create_spdif_out_ctls(codec, + per_pin->pin_nid, + per_pin->mux_nids[0]); if (err < 0) return err; + snd_hda_spdif_ctls_unassign(codec, pin_idx); } return 0; @@ -1106,13 +1184,19 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) static int generic_hdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int i; + int pin_idx; - for (i = 0; spec->pin[i]; i++) { - hdmi_enable_output(codec, spec->pin[i]); - snd_hda_codec_write(codec, spec->pin[i], 0, + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + hda_nid_t pin_nid = per_pin->pin_nid; + struct hdmi_eld *eld = &per_pin->sink_eld; + + hdmi_init_pin(codec, pin_nid); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | spec->pin[i]); + AC_USRSP_EN | pin_nid); + + snd_hda_eld_proc_new(codec, eld, pin_idx); } return 0; } @@ -1120,10 +1204,14 @@ static int generic_hdmi_init(struct hda_codec *codec) static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int i; + int pin_idx; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; + struct hdmi_eld *eld = &per_pin->sink_eld; - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); + snd_hda_eld_proc_free(codec, eld); + } snd_hda_input_jack_free(codec); kfree(spec); @@ -1140,7 +1228,6 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { static int patch_generic_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; - int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -1154,15 +1241,69 @@ static int patch_generic_hdmi(struct hda_codec *codec) } codec->patch_ops = generic_hdmi_patch_ops; - for (i = 0; i < spec->num_pins; i++) - snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); - init_channel_allocations(); return 0; } /* + * Shared non-generic implementations + */ + +static int simple_playback_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + int i; + + codec->num_pcms = spec->num_cvts; + codec->pcm_info = info; + + for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + struct hda_pcm_stream *pstr; + + chans = get_wcaps(codec, spec->cvts[i].cvt_nid); + chans = get_wcaps_channels(chans); + + info->name = generic_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + snd_BUG_ON(!spec->pcm_playback); + *pstr = *spec->pcm_playback; + pstr->nid = spec->cvts[i].cvt_nid; + if (pstr->channels_max <= 2 && chans && chans <= 16) + pstr->channels_max = chans; + } + + return 0; +} + +static int simple_playback_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err; + int i; + + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->cvts[i].cvt_nid, + spec->cvts[i].cvt_nid); + if (err < 0) + return err; + } + + return 0; +} + +static void simple_playback_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + kfree(spec); +} + +/* * Nvidia specific implementations */ @@ -1352,6 +1493,9 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, int chs; unsigned int dataDCC1, dataDCC2, channel_id; int i; + struct hdmi_spec *spec = codec->spec; + struct hda_spdif_out *spdif = + snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); mutex_lock(&codec->spdif_mutex); @@ -1361,12 +1505,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, dataDCC2 = 0x2; /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, @@ -1378,12 +1522,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, /* turn on again (if needed) */ /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); + spdif->ctls & 0xff); snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, @@ -1400,12 +1544,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, *otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) + (spdif->ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], @@ -1421,12 +1565,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, /* turn on again (if needed) */ /* enable and set the channel status audio/data flag */ if (codec->spdif_status_reset && - (codec->spdif_ctls & AC_DIG1_ENABLE)) { + (spdif->ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); + spdif->ctls & 0xff); snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, @@ -1471,17 +1615,17 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = { }; static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = nvhdmi_7x_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; static const struct hda_codec_ops nvhdmi_patch_ops_2ch = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = nvhdmi_7x_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; static int patch_nvhdmi_2ch(struct hda_codec *codec) @@ -1498,7 +1642,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; spec->num_cvts = 1; - spec->cvt[0] = nvhdmi_master_con_nid_7x; + spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x; spec->pcm_playback = &nvhdmi_pcm_playback_2ch; codec->patch_ops = nvhdmi_patch_ops_2ch; @@ -1549,11 +1693,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, substream); if (err < 0) return err; - snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT, - chans - 1); + snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); /* FIXME: XXX */ for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, spec->cvt[0], 0, + snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); } @@ -1584,18 +1728,18 @@ static int atihdmi_init(struct hda_codec *codec) snd_hda_sequence_write(codec, atihdmi_basic_init); /* SI codec requires to unmute the pin */ - if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, spec->pin[0], 0, + if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); return 0; } static const struct hda_codec_ops atihdmi_patch_ops = { - .build_controls = generic_hdmi_build_controls, - .build_pcms = generic_hdmi_build_pcms, + .build_controls = simple_playback_build_controls, + .build_pcms = simple_playback_build_pcms, .init = atihdmi_init, - .free = generic_hdmi_free, + .free = simple_playback_free, }; @@ -1613,8 +1757,8 @@ static int patch_atihdmi(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.dig_out_nid = ATIHDMI_CVT_NID; spec->num_cvts = 1; - spec->cvt[0] = ATIHDMI_CVT_NID; - spec->pin[0] = ATIHDMI_PIN_NID; + spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID; + spec->pins[0].pin_nid = ATIHDMI_PIN_NID; spec->pcm_playback = &atihdmi_pcm_digital_playback; codec->patch_ops = atihdmi_patch_ops; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b48fb43b5448..e125c60fe352 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1,7 +1,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for ALC 260/880/882 codecs + * HD audio interface patch for Realtek ALC codecs * * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw> * PeiSen Hou <pshou@realtek.com.tw> @@ -33,236 +33,11 @@ #include "hda_local.h" #include "hda_beep.h" -#define ALC880_FRONT_EVENT 0x01 -#define ALC880_DCVOL_EVENT 0x02 -#define ALC880_HP_EVENT 0x04 -#define ALC880_MIC_EVENT 0x08 - -/* ALC880 board config type */ -enum { - 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, - ALC880_LG_LW, - ALC880_MEDION_RIM, -#ifdef CONFIG_SND_DEBUG - ALC880_TEST, -#endif - ALC880_AUTO, - ALC880_MODEL_LAST /* last tag */ -}; - -/* ALC260 models */ -enum { - ALC260_BASIC, - ALC260_HP, - ALC260_HP_DC7600, - ALC260_HP_3013, - ALC260_FUJITSU_S702X, - ALC260_ACER, - ALC260_WILL, - ALC260_REPLACER_672V, - ALC260_FAVORIT100, -#ifdef CONFIG_SND_DEBUG - ALC260_TEST, -#endif - ALC260_AUTO, - ALC260_MODEL_LAST /* last tag */ -}; - -/* ALC262 models */ -enum { - ALC262_BASIC, - ALC262_HIPPO, - ALC262_HIPPO_1, - ALC262_FUJITSU, - ALC262_HP_BPC, - ALC262_HP_BPC_D7000_WL, - ALC262_HP_BPC_D7000_WF, - ALC262_HP_TC_T5735, - ALC262_HP_RP5700, - ALC262_BENQ_ED8, - ALC262_SONY_ASSAMD, - ALC262_BENQ_T31, - ALC262_ULTRA, - ALC262_LENOVO_3000, - ALC262_NEC, - ALC262_TOSHIBA_S06, - ALC262_TOSHIBA_RX1, - ALC262_TYAN, - ALC262_AUTO, - ALC262_MODEL_LAST /* last tag */ -}; - -/* ALC268 models */ -enum { - ALC267_QUANTA_IL1, - ALC268_3ST, - ALC268_TOSHIBA, - ALC268_ACER, - ALC268_ACER_DMIC, - ALC268_ACER_ASPIRE_ONE, - ALC268_DELL, - ALC268_ZEPTO, -#ifdef CONFIG_SND_DEBUG - ALC268_TEST, -#endif - ALC268_AUTO, - ALC268_MODEL_LAST /* last tag */ -}; - -/* ALC269 models */ -enum { - ALC269_BASIC, - ALC269_QUANTA_FL1, - ALC269_AMIC, - ALC269_DMIC, - ALC269VB_AMIC, - ALC269VB_DMIC, - ALC269_FUJITSU, - ALC269_LIFEBOOK, - ALC271_ACER, - ALC269_AUTO, - ALC269_MODEL_LAST /* last tag */ -}; - -/* ALC861 models */ -enum { - ALC861_3ST, - ALC660_3ST, - ALC861_3ST_DIG, - ALC861_6ST_DIG, - ALC861_UNIWILL_M31, - ALC861_TOSHIBA, - ALC861_ASUS, - ALC861_ASUS_LAPTOP, - ALC861_AUTO, - ALC861_MODEL_LAST, -}; - -/* ALC861-VD models */ -enum { - ALC660VD_3ST, - ALC660VD_3ST_DIG, - ALC660VD_ASUS_V1S, - ALC861VD_3ST, - ALC861VD_3ST_DIG, - ALC861VD_6ST_DIG, - ALC861VD_LENOVO, - ALC861VD_DALLAS, - ALC861VD_HP, - ALC861VD_AUTO, - ALC861VD_MODEL_LAST, -}; - -/* ALC662 models */ -enum { - ALC662_3ST_2ch_DIG, - ALC662_3ST_6ch_DIG, - ALC662_3ST_6ch, - ALC662_5ST_DIG, - ALC662_LENOVO_101E, - ALC662_ASUS_EEEPC_P701, - ALC662_ASUS_EEEPC_EP20, - ALC663_ASUS_M51VA, - ALC663_ASUS_G71V, - ALC663_ASUS_H13, - ALC663_ASUS_G50V, - ALC662_ECS, - ALC663_ASUS_MODE1, - ALC662_ASUS_MODE2, - ALC663_ASUS_MODE3, - ALC663_ASUS_MODE4, - ALC663_ASUS_MODE5, - ALC663_ASUS_MODE6, - ALC663_ASUS_MODE7, - ALC663_ASUS_MODE8, - ALC272_DELL, - ALC272_DELL_ZM1, - ALC272_SAMSUNG_NC10, - ALC662_AUTO, - ALC662_MODEL_LAST, -}; - -/* ALC882 models */ -enum { - ALC882_3ST_DIG, - ALC882_6ST_DIG, - ALC882_ARIMA, - ALC882_W2JC, - ALC882_TARGA, - ALC882_ASUS_A7J, - ALC882_ASUS_A7M, - ALC885_MACPRO, - ALC885_MBA21, - ALC885_MBP3, - ALC885_MB5, - ALC885_MACMINI3, - ALC885_IMAC24, - ALC885_IMAC91, - ALC883_3ST_2ch_DIG, - ALC883_3ST_6ch_DIG, - ALC883_3ST_6ch, - ALC883_6ST_DIG, - ALC883_TARGA_DIG, - ALC883_TARGA_2ch_DIG, - ALC883_TARGA_8ch_DIG, - ALC883_ACER, - ALC883_ACER_ASPIRE, - ALC888_ACER_ASPIRE_4930G, - ALC888_ACER_ASPIRE_6530G, - ALC888_ACER_ASPIRE_8930G, - ALC888_ACER_ASPIRE_7730G, - ALC883_MEDION, - ALC883_MEDION_WIM2160, - ALC883_LAPTOP_EAPD, - ALC883_LENOVO_101E_2ch, - ALC883_LENOVO_NB0763, - ALC888_LENOVO_MS7195_DIG, - ALC888_LENOVO_SKY, - ALC883_HAIER_W66, - ALC888_3ST_HP, - ALC888_6ST_DELL, - ALC883_MITAC, - ALC883_CLEVO_M540R, - ALC883_CLEVO_M720, - ALC883_FUJITSU_PI2515, - ALC888_FUJITSU_XA3530, - ALC883_3ST_6ch_INTEL, - ALC889A_INTEL, - ALC889_INTEL, - ALC888_ASUS_M90V, - ALC888_ASUS_EEE1601, - ALC889A_MB31, - ALC1200_ASUS_P5Q, - ALC883_SONY_VAIO_TT, - ALC882_AUTO, - ALC882_MODEL_LAST, -}; - -/* ALC680 models */ -enum { - ALC680_BASE, - ALC680_AUTO, - ALC680_MODEL_LAST, -}; +/* unsol event tags */ +#define ALC_FRONT_EVENT 0x01 +#define ALC_DCVOL_EVENT 0x02 +#define ALC_HP_EVENT 0x04 +#define ALC_MIC_EVENT 0x08 /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -276,14 +51,6 @@ enum { ALC_INIT_GPIO3, }; -struct alc_mic_route { - hda_nid_t pin; - unsigned char mux_idx; - unsigned char amix_idx; -}; - -#define MUX_IDX_UNDEF ((unsigned char)-1) - struct alc_customize_define { unsigned int sku_cfg; unsigned char port_connectivity; @@ -348,9 +115,9 @@ struct alc_spec { const hda_nid_t *adc_nids; const hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ /* capture setup for dynamic dual-adc switch */ - unsigned int cur_adc_idx; hda_nid_t cur_adc; unsigned int cur_adc_stream_tag; unsigned int cur_adc_format; @@ -359,9 +126,9 @@ struct alc_spec { unsigned int num_mux_defs; const struct hda_input_mux *input_mux; unsigned int cur_mux[3]; - struct alc_mic_route ext_mic; - struct alc_mic_route dock_mic; - struct alc_mic_route int_mic; + hda_nid_t ext_mic_pin; + hda_nid_t dock_mic_pin; + hda_nid_t int_mic_pin; /* channel model */ const struct hda_channel_mode *channel_mode; @@ -381,6 +148,9 @@ struct alc_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ /* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -395,6 +165,7 @@ struct alc_spec { unsigned int line_jack_present:1; unsigned int master_mute:1; unsigned int auto_mic:1; + unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ unsigned int automute:1; /* HP automute enabled */ unsigned int detect_line:1; /* Line-out detection enabled */ unsigned int automute_lines:1; /* automute line-out as well */ @@ -402,8 +173,9 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ - unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int single_input_src:1; + unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ /* auto-mute control */ int automute_mode; @@ -432,39 +204,23 @@ struct alc_spec { struct alc_multi_io multi_io[4]; }; -/* - * 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 -}; +#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) +{ + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} + +#define nid_has_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) /* * input MUX handling @@ -493,388 +249,83 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, return 0; } -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; + + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +} + +/* select the given imux item; either unmute exclusively or select the route */ +static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx, bool force) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx; - hda_nid_t nid = spec->capsrc_nids ? - spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; - unsigned int type; + int i, type; + hda_nid_t nid; mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; if (!imux->num_items && mux_idx > 0) imux = &spec->input_mux[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[adc_idx] == idx && !force) + return 0; + spec->cur_mux[adc_idx] = idx; + + if (spec->dyn_adc_switch) { + alc_dyn_adc_pcm_resetup(codec, idx); + adc_idx = spec->dyn_adc_idx[idx]; + } + + nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; + + /* no selection? */ + if (snd_hda_get_conn_list(codec, nid, NULL) <= 1) + return 1; + type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_AUD_MIX) { /* Matrix-mixer style (e.g. ALC882) */ - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; for (i = 0; i < imux->num_items; i++) { unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, imux->items[i].index, HDA_AMP_MUTE, v); } - *cur_val = idx; - return 1; } else { /* MUX style (e.g. ALC880) */ - return snd_hda_input_mux_put(codec, imux, ucontrol, nid, - &spec->cur_mux[adc_idx]); - } -} - -/* - * 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); - } + AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); } - 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; + return 1; } -static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int alc_mux_enum_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; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return alc_mux_select(codec, adc_idx, + ucontrol->value.enumerated.item[0], false); } -#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 */ - /* * set up the input pin config (depending on the given auto-pin type) */ @@ -903,29 +354,10 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } -static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!cfg->line_outs) { - while (cfg->line_outs < AUTO_CFG_MAX_OUTS && - cfg->line_out_pins[cfg->line_outs]) - cfg->line_outs++; - } - if (!cfg->speaker_outs) { - while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS && - cfg->speaker_pins[cfg->speaker_outs]) - cfg->speaker_outs++; - } - if (!cfg->hp_outs) { - while (cfg->hp_outs < AUTO_CFG_MAX_OUTS && - cfg->hp_pins[cfg->hp_outs]) - cfg->hp_outs++; - } -} - /* + * Append the given mixer and verb elements for the later use + * The mixer array is referred in build_controls(), and init_verbs are + * called in init(). */ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix) { @@ -942,61 +374,8 @@ static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) } /* - * set up from the preset table + * GPIO setup tables, used in initialization */ -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); -} - /* Enable GPIO mask and set output */ static const struct hda_verb alc_gpio1_init_verbs[] = { {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, @@ -1051,14 +430,19 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } +/* + * Jack-reporting via input-jack layer + */ + +/* initialization of jacks; currently checks only a few known pins */ static int alc_init_jacks(struct hda_codec *codec) { #ifdef CONFIG_SND_HDA_INPUT_JACK struct alc_spec *spec = codec->spec; int err; unsigned int hp_nid = spec->autocfg.hp_pins[0]; - unsigned int mic_nid = spec->ext_mic.pin; - unsigned int dock_nid = spec->dock_mic.pin; + unsigned int mic_nid = spec->ext_mic_pin; + unsigned int dock_nid = spec->dock_mic_pin; if (hp_nid) { err = snd_hda_input_jack_add(codec, hp_nid, @@ -1086,7 +470,12 @@ static int alc_init_jacks(struct hda_codec *codec) return 0; } -static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +/* + * Jack detections for HP auto-mute and mic-switch + */ + +/* check each pin in the given array; returns true if any of them is plugged */ +static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) { int i, present = 0; @@ -1100,6 +489,7 @@ static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) return present; } +/* standard HP/line-out auto-mute helper */ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool mute, bool hp_out) { @@ -1170,6 +560,7 @@ static void update_speakers(struct hda_codec *codec) spec->autocfg.line_out_pins, on, false); } +/* standard HP-automute helper */ static void alc_hp_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1182,6 +573,7 @@ static void alc_hp_automute(struct hda_codec *codec) update_speakers(codec); } +/* standard line-out-automute helper */ static void alc_line_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1194,106 +586,33 @@ static void alc_line_automute(struct hda_codec *codec) update_speakers(codec); } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - return -1; -} - -/* switch the current ADC according to the jack state */ -static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int present; - hda_nid_t new_adc; - - present = snd_hda_jack_detect(codec, spec->ext_mic.pin); - if (present) - spec->cur_adc_idx = 1; - else - spec->cur_adc_idx = 0; - new_adc = spec->adc_nids[spec->cur_adc_idx]; - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } -} +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) +/* standard mic auto-switch helper */ static void alc_mic_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct alc_mic_route *dead1, *dead2, *alive; - unsigned int present, type; - hda_nid_t cap_nid; + hda_nid_t *pins = spec->imux_pins; - if (!spec->auto_mic) - return; - if (!spec->int_mic.pin || !spec->ext_mic.pin) + if (!spec->auto_mic || !spec->auto_mic_valid_imux) return; if (snd_BUG_ON(!spec->adc_nids)) return; - - if (spec->dual_adc_switch) { - alc_dual_mic_adc_auto_switch(codec); + if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return; - } - - cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; - - alive = &spec->int_mic; - dead1 = &spec->ext_mic; - dead2 = &spec->dock_mic; - - present = snd_hda_jack_detect(codec, spec->ext_mic.pin); - if (present) { - alive = &spec->ext_mic; - dead1 = &spec->int_mic; - dead2 = &spec->dock_mic; - } - if (!present && spec->dock_mic.pin > 0) { - present = snd_hda_jack_detect(codec, spec->dock_mic.pin); - if (present) { - alive = &spec->dock_mic; - dead1 = &spec->int_mic; - dead2 = &spec->ext_mic; - } - snd_hda_input_jack_report(codec, spec->dock_mic.pin); - } - type = get_wcaps_type(get_wcaps(codec, cap_nid)); - if (type == AC_WID_AUD_MIX) { - /* Matrix-mixer style (e.g. ALC882) */ - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - alive->mux_idx, - HDA_AMP_MUTE, 0); - if (dead1->pin > 0) - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - dead1->mux_idx, - HDA_AMP_MUTE, HDA_AMP_MUTE); - if (dead2->pin > 0) - snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, - dead2->mux_idx, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* MUX style (e.g. ALC880) */ - snd_hda_codec_write_cache(codec, cap_nid, 0, - AC_VERB_SET_CONNECT_SEL, - alive->mux_idx); - } - snd_hda_input_jack_report(codec, spec->ext_mic.pin); + if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) + alc_mux_select(codec, 0, spec->ext_mic_idx, false); + else if (spec->dock_mic_idx >= 0 && + snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) + alc_mux_select(codec, 0, spec->dock_mic_idx, false); + else + alc_mux_select(codec, 0, spec->int_mic_idx, false); - /* FIXME: analog mixer */ + snd_hda_input_jack_report(codec, pins[spec->ext_mic_idx]); + if (spec->dock_mic_idx >= 0) + snd_hda_input_jack_report(codec, pins[spec->dock_mic_idx]); } /* unsolicited event for HP jack sensing */ @@ -1304,18 +623,19 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) else res >>= 26; switch (res) { - case ALC880_HP_EVENT: + case ALC_HP_EVENT: alc_hp_automute(codec); break; - case ALC880_FRONT_EVENT: + case ALC_FRONT_EVENT: alc_line_automute(codec); break; - case ALC880_MIC_EVENT: + case ALC_MIC_EVENT: alc_mic_automute(codec); break; } } +/* call init functions of standard auto-mute helpers */ static void alc_inithook(struct hda_codec *codec) { alc_hp_automute(codec); @@ -1341,6 +661,7 @@ static void alc888_coef_init(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x3030); } +/* additional initialization for ALC889 variants */ static void alc889_coef_init(struct hda_codec *codec) { unsigned int tmp; @@ -1365,28 +686,12 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) { /* We currently only handle front, HP */ - switch (codec->vendor_id) { - case 0x10ec0260: - set_eapd(codec, 0x0f, on); - set_eapd(codec, 0x10, on); - break; - case 0x10ec0262: - case 0x10ec0267: - case 0x10ec0268: - case 0x10ec0269: - case 0x10ec0270: - case 0x10ec0272: - case 0x10ec0660: - case 0x10ec0662: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0862: - case 0x10ec0889: - case 0x10ec0892: - set_eapd(codec, 0x14, on); - set_eapd(codec, 0x15, on); - break; - } + static hda_nid_t pins[] = { + 0x0f, 0x10, 0x14, 0x15, 0 + }; + hda_nid_t *p; + for (p = pins; *p; p++) + set_eapd(codec, *p, on); } /* generic shutup callback; @@ -1398,10 +703,12 @@ static void alc_eapd_shutup(struct hda_codec *codec) msleep(200); } +/* generic EAPD initialization */ static void alc_auto_init_amp(struct hda_codec *codec, int type) { unsigned int tmp; + alc_auto_setup_eapd(codec, true); switch (type) { case ALC_INIT_GPIO1: snd_hda_sequence_write(codec, alc_gpio1_init_verbs); @@ -1413,7 +720,6 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case ALC_INIT_DEFAULT: - alc_auto_setup_eapd(codec, true); switch (codec->vendor_id) { case 0x10ec0260: snd_hda_codec_write(codec, 0x1a, 0, @@ -1457,6 +763,9 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } } +/* + * Auto-Mute mode mixer enum support + */ static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1543,7 +852,11 @@ static const struct snd_kcontrol_new alc_automute_mode_enum = { .put = alc_automute_mode_put, }; -static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec); +static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec) +{ + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + return snd_array_new(&spec->kctls); +} static int alc_add_automute_mode_enum(struct hda_codec *codec) { @@ -1560,6 +873,10 @@ static int alc_add_automute_mode_enum(struct hda_codec *codec) return 0; } +/* + * Check the availability of HP/line-out auto-mute; + * Set up appropriately if really supported + */ static void alc_init_auto_hp(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1578,13 +895,15 @@ static void alc_init_auto_hp(struct hda_codec *codec) if (present == 3) spec->automute_hp_lo = 1; /* both HP and LO automute */ - if (!cfg->speaker_pins[0]) { + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = cfg->line_outs; } - if (!cfg->hp_pins[0]) { + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { memcpy(cfg->hp_pins, cfg->line_out_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = cfg->line_outs; @@ -1598,11 +917,12 @@ static void alc_init_auto_hp(struct hda_codec *codec) nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_HP_EVENT); + AC_USRSP_EN | ALC_HP_EVENT); spec->automute = 1; spec->automute_mode = ALC_AUTOMUTE_PIN; } if (spec->automute && cfg->line_out_pins[0] && + cfg->speaker_pins[0] && cfg->line_out_pins[0] != cfg->hp_pins[0] && cfg->line_out_pins[0] != cfg->speaker_pins[0]) { for (i = 0; i < cfg->line_outs; i++) { @@ -1613,7 +933,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) "on NID 0x%x\n", nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_FRONT_EVENT); + AC_USRSP_EN | ALC_FRONT_EVENT); spec->detect_line = 1; } spec->automute_lines = spec->detect_line; @@ -1626,6 +946,132 @@ static void alc_init_auto_hp(struct hda_codec *codec) } } +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_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 i; + return -1; +} + +/* check whether dynamic ADC-switching is available */ +static bool alc_check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, n, idx; + hda_nid_t cap, pin; + + if (imux != spec->input_mux) /* no dynamic imux? */ + return false; + + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!pin) + return false; + if (get_connection_index(codec, cap, pin) < 0) + break; + } + if (i >= imux->num_items) + return true; /* no ADC-switch is needed */ + } + + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + imux->items[i].index = idx; + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + 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]) + 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) +{ + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + + if (!spec->auto_mic) + return false; + if (spec->auto_mic_valid_imux) + return true; /* already checked */ + + /* fill up imux indices */ + if (!alc_check_dyn_adc_switch(codec)) { + spec->auto_mic = 0; + return false; + } + + imux = spec->input_mux; + spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, + spec->imux_pins, imux->num_items); + spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, + spec->imux_pins, imux->num_items); + spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, + spec->imux_pins, imux->num_items); + if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { + spec->auto_mic = 0; + return false; /* no corresponding imux */ + } + + snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC_MIC_EVENT); + if (spec->dock_mic_pin) + snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC_MIC_EVENT); + + spec->auto_mic_valid_imux = 1; + spec->auto_mic = 1; + return true; +} + +/* + * Check the availability of auto-mic switch; + * Set up if really supported + */ static void alc_init_auto_mic(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1633,6 +1079,8 @@ static void alc_init_auto_mic(struct hda_codec *codec) hda_nid_t fixed, ext, dock; int i; + spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; + fixed = ext = dock = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; @@ -1674,21 +1122,32 @@ static void alc_init_auto_mic(struct hda_codec *codec) return; /* no unsol support */ if (dock && !is_jack_detectable(codec, dock)) return; /* no unsol support */ + + /* check imux indices */ + spec->ext_mic_pin = ext; + spec->int_mic_pin = fixed; + spec->dock_mic_pin = dock; + + spec->auto_mic = 1; + if (!alc_auto_mic_check_imux(codec)) + return; + snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", ext, fixed, dock); - spec->ext_mic.pin = ext; - spec->dock_mic.pin = dock; - spec->int_mic.pin = fixed; - spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->dock_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ - spec->auto_mic = 1; - snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_MIC_EVENT); spec->unsol_event = alc_sku_unsol_event; } +/* check the availabilities of auto-mute and auto-mic switches */ +static void alc_auto_check_switches(struct hda_codec *codec) +{ + alc_init_auto_hp(codec); + alc_init_auto_mic(codec); +} + +/* + * Realtek SSID verification + */ + /* Could be any non-zero and even value. When used as fixup, tells * the driver to ignore any present sku defines. */ @@ -1759,6 +1218,12 @@ do_sku: return 0; } +/* return true if the given NID is found in the list */ +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + return find_idx_in_nid_list(nid, list, nums) >= 0; +} + /* check subsystem ID and set up device-specific initialization; * return 1 if initialized, 0 if invalid SSID */ @@ -1868,27 +1333,24 @@ do_sku: nid = porti; else return 1; - for (i = 0; i < spec->autocfg.line_outs; i++) - if (spec->autocfg.line_out_pins[i] == nid) - return 1; + if (found_in_nid_list(nid, spec->autocfg.line_out_pins, + spec->autocfg.line_outs)) + return 1; spec->autocfg.hp_pins[0] = nid; } return 1; } -static void alc_ssid_check(struct hda_codec *codec, - hda_nid_t porta, hda_nid_t porte, - hda_nid_t portd, hda_nid_t porti) +/* Check the validity of ALC subsystem-id + * ports contains an array of 4 pin NIDs for port-A, E, D and I */ +static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) { - if (!alc_subsystem_id(codec, porta, porte, portd, porti)) { + if (!alc_subsystem_id(codec, ports[0], ports[1], ports[2], ports[3])) { struct alc_spec *spec = codec->spec; snd_printd("realtek: " "Enable default setup for auto mode as fallback\n"); spec->init_amp = ALC_INIT_DEFAULT; } - - alc_init_auto_hp(codec); - alc_init_auto_mic(codec); } /* @@ -2036,6 +1498,9 @@ static void alc_pick_fixup(struct hda_codec *codec, } } +/* + * COEF access helper functions + */ static int alc_read_coef_idx(struct hda_codec *codec, unsigned int coef_idx) { @@ -2056,20 +1521,32 @@ static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, coef_val); } +/* + * Digital I/O handling + */ + /* set right pin controls for digital I/O */ static void alc_auto_init_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; - hda_nid_t pin; + hda_nid_t pin, dac; for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - } + if (!pin) + continue; + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (!i) + dac = spec->multiout.dig_out_nid; + else + dac = spec->slave_dig_outs[i - 1]; + if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) + continue; + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); } pin = spec->autocfg.dig_in_pin; if (pin) @@ -2087,11 +1564,13 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* support multiple SPDIFs; the secondary is set up as a slave */ for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t conn[4]; err = snd_hda_get_connections(codec, spec->autocfg.dig_out_pins[i], - &dig_nid, 1); + conn, ARRAY_SIZE(conn)); if (err < 0) continue; + dig_nid = conn[0]; /* assume the first element is audio-out */ if (!i) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -2124,572 +1603,22 @@ static void alc_auto_parse_digital(struct hda_codec *codec) } /* - * ALC888 - */ - -/* - * 2ch mode + * capture mixer elements */ -static const struct hda_verb alc888_4ST_ch2_intel_init[] = { -/* Mic-in jack as mic in */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-in jack as Line in */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-Out as Front */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc888_4ST_ch4_intel_init[] = { -/* Mic-in jack as mic in */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as Front */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc888_4ST_ch6_intel_init[] = { -/* Mic-in jack as CLFE */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc888_4ST_ch8_intel_init[] = { -/* Mic-in jack as CLFE */ - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-in jack as Surround */ - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, -/* Line-Out as Side */ - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - { } /* end */ -}; - -static const struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { - { 2, alc888_4ST_ch2_intel_init }, - { 4, alc888_4ST_ch4_intel_init }, - { 6, alc888_4ST_ch6_intel_init }, - { 8, alc888_4ST_ch8_intel_init }, -}; - -/* - * ALC888 Fujitsu Siemens Amillo xa3530 - */ - -static const struct hda_verb alc888_fujitsu_xa3530_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Connect Internal HP to Front */ - {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}, -/* Connect Bass HP to Front */ - {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, 0x00}, -/* Connect Line-Out side jack (SPDIF) to Side */ - {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}, -/* Connect Mic jack to CLFE */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, -/* Connect Line-in jack to Surround */ - {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}, -/* Connect HP out jack to Front */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Enable unsolicited event for HP jack and Line-out jack */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {} -}; - -static void alc889_automute_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->autocfg.speaker_pins[3] = 0x19; - spec->autocfg.speaker_pins[4] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc889_intel_init_hook(struct hda_codec *codec) -{ - alc889_coef_init(codec); - alc_hp_automute(codec); -} - -static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x17; /* line-out */ - spec->autocfg.hp_pins[1] = 0x1b; /* hp */ - spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ - spec->autocfg.speaker_pins[1] = 0x15; /* bass */ - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * ALC888 Acer Aspire 4930G model - */ - -static const struct hda_verb alc888_acer_aspire_4930g_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Connect Internal HP to front */ - {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}, -/* Connect HP out to front */ - {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, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - * ALC888 Acer Aspire 6530G model - */ - -static const struct hda_verb alc888_acer_aspire_6530g_verbs[] = { -/* Route to built-in subwoofer as well as speakers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, 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)}, -/* Bias voltage on for external mic port */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Enable speaker output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/* Enable headphone output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - *ALC888 Acer Aspire 7730G model - */ - -static const struct hda_verb alc888_acer_aspire_7730G_verbs[] = { -/* Bias voltage on for external mic port */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Enable speaker output */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/* Enable headphone output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, -/*Enable internal subwoofer */ - {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, 0x02}, - {0x17, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* - * ALC889 Acer Aspire 8930G model - */ - -static const struct hda_verb alc889_acer_aspire_8930g_verbs[] = { -/* Front Mic: set to PIN_IN (empty by default) */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -/* Unselect Front Mic by default in input mixer 3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, -/* Enable unsolicited event for HP jack */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -/* Connect Internal Front to Front */ - {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}, -/* Connect Internal Rear to Rear */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, -/* Connect Internal CLFE to CLFE */ - {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}, -/* Connect HP out to Front */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, -/* Enable all DACs */ -/* DAC DISABLE/MUTE 1? */ -/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x03}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, -/* DAC DISABLE/MUTE 2? */ -/* some bit here disables the other DACs. Init=0x4900 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, -/* DMIC fix - * This laptop has a stereo digital microphone. The mics are only 1cm apart - * which makes the stereo useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal instead of standard - * stereo, which is annoying. So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, turning it into a - * normal mono mic. - */ -/* DMIC_CONTROL? Init value = 0x0001 */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0003}, - { } -}; - -static const struct hda_input_mux alc888_2_capture_sources[2] = { - /* Front mic only available on one ADC */ - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Front Mic", 0xb }, - }, - }, - { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, - } -}; - -static const struct hda_input_mux alc888_acer_aspire_6530_sources[2] = { - /* Interal mic only available on one ADC */ - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line In", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - { "Internal Mic", 0xb }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line In", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - } -}; - -static const struct hda_input_mux alc889_capture_sources[3] = { - /* Digital mic only available on first "ADC" */ - { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Front Mic", 0xb }, - { "Input Mix", 0xa }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - }, - { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "Input Mix", 0xa }, - }, - } -}; - -static const struct snd_kcontrol_new alc888_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_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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_acer_aspire_4930g_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_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc889_acer_aspire_8930g_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("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), - { } /* end */ -}; - - -static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * 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 - -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 */ -}; - -/* capture mixer elements */ static int alc_cap_vol_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; + unsigned long val; int err; mutex_lock(&codec->control_mutex); - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, - HDA_INPUT); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); mutex_unlock(&codec->control_mutex); return err; @@ -2700,11 +1629,15 @@ static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + unsigned long val; int err; mutex_lock(&codec->control_mutex); - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, - HDA_INPUT); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); mutex_unlock(&codec->control_mutex); return err; @@ -2722,7 +1655,7 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, int i, err = 0; mutex_lock(&codec->control_mutex); - if (check_adc_switch && spec->dual_adc_switch) { + if (check_adc_switch && spec->dyn_adc_switch) { for (i = 0; i < spec->num_adc_nids; i++) { kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], @@ -2733,9 +1666,14 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, } } else { i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); + if (spec->vol_in_capsrc) + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], + 3, 0, HDA_OUTPUT); + else + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], + 3, 0, HDA_INPUT); err = func(kcontrol, ucontrol); } error: @@ -2830,335 +1768,6 @@ DEFINE_CAPMIX_NOSRC(2); DEFINE_CAPMIX_NOSRC(3); /* - * 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 */ -}; - -/* * virtual master controls */ @@ -3237,6 +1846,7 @@ static int alc_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; @@ -3304,7 +1914,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } } - if (spec->cap_mixer) { + if (spec->cap_mixer && spec->adc_nids) { const char *kname = kctl ? kctl->id.name : NULL; for (knew = spec->cap_mixer; knew->name; knew++) { if (kname && strcmp(knew->name, kname) == 0) @@ -3368,789 +1978,6 @@ static int alc_build_controls(struct hda_codec *codec) /* - * 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 | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_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 | ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT}, - - { } -}; - -static const struct hda_verb alc880_beep_init_verbs[] = { - { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) }, - { } -}; - -/* 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); -} - -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; - spec->automute = 1; - spec->automute_mode = 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! - */ - switch (res >> 28) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } -} - -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; - spec->automute = 1; - spec->automute_mode = 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! - */ - if ((res >> 28) == ALC880_DCVOL_EVENT) - alc880_uniwill_p53_dcvol_automute(codec); - else - alc_sku_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|ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_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 | ALC880_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; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* - * LG LW20 - * - * Pin assignment: - * Speaker-out: 0x14 - * Mic-In: 0x18 - * Built-in Mic-In: 0x19 - * Line-In: 0x1b - * HP-Out: 0x1a - * SPDIF-Out: 0x1e - */ - -static const struct hda_input_mux alc880_lg_lw_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line In", 0x2 }, - }, -}; - -#define alc880_lg_lw_modes alc880_threestack_modes - -static const struct snd_kcontrol_new alc880_lg_lw_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("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("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, 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_lw_init_verbs[] = { - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ - - /* set capture source to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* speaker-out */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* HP-out */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* mic-in to input */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* built-in mic */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_lw_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc880_medion_rim_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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 Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_input_mux alc880_medion_rim_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_verb alc880_medion_rim_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}, - - /* 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_MUTE}, - /* Internal Speaker */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc880_medion_rim_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_hp_automute(codec); - /* toggle EAPD */ - if (spec->jack_present) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - else - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); -} - -static void alc880_medion_rim_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! - */ - if ((res >> 28) == ALC880_HP_EVENT) - alc880_medion_rim_automute(codec); -} - -static void alc880_medion_rim_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -#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 */ -}; - -static const struct hda_amp_list alc880_lg_loopbacks[] = { - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 6 }, - { 0x0b, HDA_INPUT, 7 }, - { } /* end */ -}; -#endif - -/* * Common callbacks */ @@ -4196,7 +2023,7 @@ static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* * Analog playback callbacks */ -static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4205,7 +2032,7 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, hinfo); } -static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4216,7 +2043,7 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } -static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4227,7 +2054,7 @@ static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, /* * Digital out */ -static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4235,7 +2062,7 @@ static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_open(codec, &spec->multiout); } -static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4246,7 +2073,7 @@ static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } -static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4254,7 +2081,7 @@ static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); } -static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, +static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4265,7 +2092,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ -static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -4278,7 +2105,7 @@ static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, return 0; } -static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4290,21 +2117,21 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, } /* analog capture with dynamic dual-adc changes */ -static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; + spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; spec->cur_adc_stream_tag = stream_tag; spec->cur_adc_format = format; snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); return 0; } -static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { @@ -4314,70 +2141,70 @@ static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } -static const struct hda_pcm_stream dualmic_pcm_analog_capture = { +static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, .nid = 0, /* fill later */ .ops = { - .prepare = dualmic_capture_pcm_prepare, - .cleanup = dualmic_capture_pcm_cleanup + .prepare = dyn_adc_capture_pcm_prepare, + .cleanup = dyn_adc_capture_pcm_cleanup }, }; /* */ -static const struct hda_pcm_stream alc880_pcm_analog_playback = { +static const struct hda_pcm_stream alc_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_playback_pcm_open, - .prepare = alc880_playback_pcm_prepare, - .cleanup = alc880_playback_pcm_cleanup + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_analog_capture = { +static const struct hda_pcm_stream alc_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ }; -static const struct hda_pcm_stream alc880_pcm_analog_alt_playback = { +static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ }; -static const struct hda_pcm_stream alc880_pcm_analog_alt_capture = { +static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { .substreams = 2, /* can be overridden */ .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .prepare = alc880_alt_capture_pcm_prepare, - .cleanup = alc880_alt_capture_pcm_cleanup + .prepare = alc_alt_capture_pcm_prepare, + .cleanup = alc_alt_capture_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_digital_playback = { +static const struct hda_pcm_stream alc_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_dig_playback_pcm_open, - .close = alc880_dig_playback_pcm_close, - .prepare = alc880_dig_playback_pcm_prepare, - .cleanup = alc880_dig_playback_pcm_cleanup + .open = alc_dig_playback_pcm_open, + .close = alc_dig_playback_pcm_close, + .prepare = alc_dig_playback_pcm_prepare, + .cleanup = alc_dig_playback_pcm_cleanup }, }; -static const struct hda_pcm_stream alc880_pcm_digital_capture = { +static const struct hda_pcm_stream alc_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -4395,6 +2222,7 @@ static int alc_build_pcms(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; + const struct hda_pcm_stream *p; int i; codec->num_pcms = 1; @@ -4407,16 +2235,22 @@ static int alc_build_pcms(struct hda_codec *codec) "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - if (spec->stream_analog_playback) { - if (snd_BUG_ON(!spec->multiout.dac_nids)) - return -EINVAL; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); + if (spec->multiout.dac_nids > 0) { + p = spec->stream_analog_playback; + if (!p) + p = &alc_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; } - if (spec->stream_analog_capture) { - if (snd_BUG_ON(!spec->adc_nids)) - return -EINVAL; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); + if (spec->adc_nids) { + p = spec->stream_analog_capture; + if (!p) { + if (spec->dyn_adc_switch) + p = &dyn_adc_pcm_analog_capture; + else + p = &alc_pcm_analog_capture; + } + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; } @@ -4443,14 +2277,18 @@ static int alc_build_pcms(struct hda_codec *codec) info->pcm_type = spec->dig_out_type; else info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid && - spec->stream_digital_playback) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); + if (spec->multiout.dig_out_nid) { + p = spec->stream_digital_playback; + if (!p) + p = &alc_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } - if (spec->dig_in_nid && - spec->stream_digital_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture); + if (spec->dig_in_nid) { + p = spec->stream_digital_capture; + if (!p) + p = &alc_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } /* FIXME: do we need this for all Realtek codec models? */ @@ -4464,14 +2302,15 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ /* Additional Analaog capture for index #2 */ - if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) || - (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) { + if (spec->alt_dac_nid || spec->num_adc_nids > 1) { codec->num_pcms = 3; info = spec->pcm_rec + 2; info->name = spec->stream_name_analog; if (spec->alt_dac_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_alt_playback; + p = spec->stream_analog_alt_playback; + if (!p) + p = &alc_pcm_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->alt_dac_nid; } else { @@ -4479,9 +2318,11 @@ static int alc_build_pcms(struct hda_codec *codec) alc_pcm_null_stream; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; } - if (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_analog_alt_capture; + if (spec->num_adc_nids > 1) { + p = spec->stream_analog_alt_capture; + if (!p) + p = &alc_pcm_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1]; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = @@ -4548,7 +2389,7 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state) } #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int alc_resume(struct hda_codec *codec) { msleep(150); /* to avoid pop noise */ @@ -4568,7 +2409,7 @@ static const struct hda_codec_ops alc_patch_ops = { .init = alc_init, .free = alc_free, .unsol_event = alc_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .resume = alc_resume, #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -4591,679 +2432,6 @@ static int alc_codec_rename(struct hda_codec *codec, const char *name) } /* - * 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", - [ALC880_LG_LW] = "lg-lw", - [ALC880_MEDION_RIM] = "medion", -#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(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM), - 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, 0x0018, "LG LW20", ALC880_LG_LW), - 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(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), - 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 = alc_sku_unsol_event, - .setup = alc880_lg_setup, - .init_hook = alc_hp_automute, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .loopbacks = alc880_lg_loopbacks, -#endif - }, - [ALC880_LG_LW] = { - .mixers = { alc880_lg_lw_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_lg_lw_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_lg_lw_modes), - .channel_mode = alc880_lg_lw_modes, - .input_mux = &alc880_lg_lw_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc880_lg_lw_setup, - .init_hook = alc_hp_automute, - }, - [ALC880_MEDION_RIM] = { - .mixers = { alc880_medion_rim_mixer }, - .init_verbs = { alc880_volume_init_verbs, - alc880_medion_rim_init_verbs, - alc_gpio2_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_medion_rim_capture_source, - .unsol_event = alc880_medion_rim_unsol_event, - .setup = alc880_medion_rim_setup, - .init_hook = alc880_medion_rim_automute, - }, -#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 -}; - -/* * Automatic parse of I/O pins from the BIOS configuration */ @@ -5272,18 +2440,12 @@ enum { ALC_CTL_WIDGET_MUTE, ALC_CTL_BIND_MUTE, }; -static const struct snd_kcontrol_new alc880_control_templates[] = { +static const struct snd_kcontrol_new alc_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), HDA_BIND_MUTE(NULL, 0, 0, 0), }; -static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec) -{ - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - return snd_array_new(&spec->kctls); -} - /* add dynamic controls */ static int add_control(struct alc_spec *spec, int type, const char *name, int cidx, unsigned long val) @@ -5293,7 +2455,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, knew = alc_kcontrol_new(spec); if (!knew) return -ENOMEM; - *knew = alc880_control_templates[type]; + *knew = alc_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; @@ -5322,60 +2484,15 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) -#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) -#define alc880_fixed_pin_idx(nid) ((nid) - 0x14) -#define alc880_is_multi_pin(nid) ((nid) >= 0x18) -#define alc880_multi_pin_idx(nid) ((nid) - 0x18) -#define alc880_idx_to_dac(nid) ((nid) + 0x02) -#define alc880_dac_to_idx(nid) ((nid) - 0x02) -#define alc880_idx_to_mixer(nid) ((nid) + 0x0c) -#define alc880_idx_to_selector(nid) ((nid) + 0x10) -#define ALC880_PIN_CD_NID 0x1c - -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc880_auto_fill_dac_nids(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int assigned[4]; - int i, j; - - memset(assigned, 0, sizeof(assigned)); - spec->multiout.dac_nids = spec->private_dac_nids; - - /* check the pins hardwired to audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (alc880_is_fixed_pin(nid)) { - int idx = alc880_fixed_pin_idx(nid); - spec->private_dac_nids[i] = alc880_idx_to_dac(idx); - assigned[idx] = 1; - } - } - /* left pins can be connect to any audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (alc880_is_fixed_pin(nid)) - continue; - /* search for an empty channel */ - for (j = 0; j < cfg->line_outs; j++) { - if (!assigned[j]) { - spec->private_dac_nids[i] = - alc880_idx_to_dac(j); - assigned[j] = 1; - break; - } - } - } - spec->multiout.num_dacs = cfg->line_outs; - return 0; -} - -static const char *alc_get_line_out_pfx(struct alc_spec *spec, - bool can_be_master) +static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, + bool can_be_master, int *index) { struct auto_pin_cfg *cfg = &spec->autocfg; + static const char * const chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; + *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && !cfg->hp_outs && !cfg->speaker_outs && can_be_master) return "Master"; @@ -5386,120 +2503,17 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, return "Speaker"; break; case AUTO_PIN_HP_OUT: + /* for multi-io case, only the primary out */ + if (ch && spec->multi_ios) + break; + *index = ch; return "Headphone"; default: if (cfg->line_outs == 1 && !spec->multi_ios) return "PCM"; break; } - return NULL; -} - -/* add playback controls from the parsed DAC table */ -static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, false); - hda_nid_t nid; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - if (!spec->multiout.dac_nids[i]) - continue; - nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); - if (!pfx && i == 2) { - /* Center/LFE */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "Center", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "LFE", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "Center", - HDA_COMPOSE_AMP_VAL(nid, 1, 2, - HDA_INPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "LFE", - HDA_COMPOSE_AMP_VAL(nid, 2, 2, - HDA_INPUT)); - if (err < 0) - return err; - } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, index, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, index, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, - HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, - const char *pfx) -{ - hda_nid_t nid; - int err; - - if (!pin) - return 0; - - if (alc880_is_fixed_pin(pin)) { - nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else - spec->multiout.extra_out_nid[0] = nid; - /* control HP volume/switch on the output mixer amp */ - nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc880_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; + return chname[ch]; } /* create input playback/capture controls for the given pin */ @@ -5526,17 +2540,72 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; } +/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ +static int alc_auto_fill_adc_caps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->private_adc_nids; + hda_nid_t *cap_nids = spec->private_capsrc_nids; + int max_nums = ARRAY_SIZE(spec->private_adc_nids); + bool indep_capsrc = false; + int i, nums = 0; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + hda_nid_t src; + const hda_nid_t *list; + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + cap_nids[nums] = nid; + src = nid; + for (;;) { + int n; + type = get_wcaps_type(get_wcaps(codec, src)); + if (type == AC_WID_PIN) + break; + if (type == AC_WID_AUD_SEL) { + cap_nids[nums] = src; + indep_capsrc = true; + break; + } + n = snd_hda_get_conn_list(codec, src, &list); + if (n > 1) { + cap_nids[nums] = src; + indep_capsrc = true; + break; + } else if (n != 1) + break; + src = *list; + } + if (++nums >= max_nums) + break; + } + spec->adc_nids = spec->private_adc_nids; + spec->capsrc_nids = spec->private_capsrc_nids; + spec->num_adc_nids = nums; + return nums; +} + /* create playback/capture controls for input pins */ -static int alc_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - hda_nid_t mixer, - hda_nid_t cap1, hda_nid_t cap2) +static int alc_auto_create_input_ctls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, type_idx = 0; + int num_adcs; + int i, c, err, idx, type_idx = 0; const char *prev_label = NULL; + num_adcs = alc_auto_fill_adc_caps(codec); + if (num_adcs < 0) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -5563,21 +2632,22 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec, } } - if (!cap1) - continue; - idx = get_connection_index(codec, cap1, pin); - if (idx < 0 && cap2) - idx = get_connection_index(codec, cap2, pin); - if (idx >= 0) - snd_hda_add_imux_item(imux, label, idx, NULL); + for (c = 0; c < num_adcs; c++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[c] : spec->adc_nids[c]; + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + break; + } + } } - return 0; -} -static int alc880_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09); + spec->num_mux_defs = 1; + spec->input_mux = imux; + + return 0; } static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, @@ -5586,25 +2656,11 @@ static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); /* unmute pin */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) -{ - alc_set_pin_output(codec, nid, pin_type); - /* need the manual connection? */ - if (alc880_is_multi_pin(nid)) { - struct alc_spec *spec = codec->spec; - int idx = alc880_multi_pin_idx(nid); - snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, - AC_VERB_SET_CONNECT_SEL, - alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); - } -} - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -5613,177 +2669,732 @@ static int get_pin_type(int line_out_type) return PIN_OUT; } -static void alc880_auto_init_multi_out(struct hda_codec *codec) +static void alc_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc880_auto_set_output_and_unmute(codec, nid, pin_type, i); + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (alc_is_input_pin(codec, nid)) { + alc_set_input_pin(codec, nid, cfg->inputs[i].type); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } + } + + /* mute all loopback inputs */ + if (spec->mixer_nid) { + int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL); + for (i = 0; i < nums; i++) + snd_hda_codec_write(codec, spec->mixer_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } +} + +/* convert from MIX nid to DAC */ +static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) +{ + hda_nid_t list[5]; + int i, num; + + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) + return nid; + num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); + for (i = 0; i < num; i++) { + if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) + return list[i]; } + return 0; +} + +/* go down to the selector widget before the mixer */ +static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t srcs[5]; + int num = snd_hda_get_connections(codec, pin, srcs, + ARRAY_SIZE(srcs)); + if (num != 1 || + get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) + return pin; + return srcs[0]; } -static void alc880_auto_init_extra_out(struct hda_codec *codec) +/* get MIX nid connected to the given pin targeted to DAC */ +static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + hda_nid_t mix[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) + return mix[i]; + } + return 0; +} + +/* select the connection from pin to DAC if needed */ +static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + hda_nid_t mix[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + if (num < 2) + return 0; + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, i); + return 0; + } + } + return 0; +} + +/* 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 pin; + hda_nid_t srcs[5]; + int i, num; - pin = spec->autocfg.speaker_pins[0]; - if (pin) /* connect to front */ - alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + 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++) { + 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, + spec->multiout.num_dacs)) + continue; + if (spec->multiout.hp_nid == nid) + continue; + if (found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + continue; + return nid; + } + return 0; +} + +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t sel = alc_go_down_to_selector(codec, pin); + if (snd_hda_get_conn_list(codec, sel, NULL) == 1) + return alc_auto_look_for_dac(codec, pin); + return 0; } -static void alc880_auto_init_analog_input(struct hda_codec *codec) +/* fill in the dac_nids table from the parsed pin configuration */ +static int alc_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + const struct auto_pin_cfg *cfg = &spec->autocfg; + bool redone = false; int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC880_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + again: + /* set num_dacs once to full for alc_auto_look_for_dac() */ + spec->multiout.num_dacs = cfg->line_outs; + spec->multiout.hp_nid = 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; + + /* 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_nid = + 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]); + } + + 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; } } + + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + } + + if (cfg->hp_outs && !spec->multiout.hp_nid) + spec->multiout.hp_nid = + alc_auto_look_for_dac(codec, cfg->hp_pins[0]); + if (cfg->speaker_outs && !spec->multiout.extra_out_nid[0]) + spec->multiout.extra_out_nid[0] = + alc_auto_look_for_dac(codec, cfg->speaker_pins[0]); + + return 0; } -static void alc880_auto_init_input_src(struct hda_codec *codec) +static int alc_auto_add_vol_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) { - struct alc_spec *spec = codec->spec; - int c; - - for (c = 0; c < spec->num_adc_nids; c++) { - unsigned int mux_idx; - const struct hda_input_mux *imux; - mux_idx = c >= spec->num_mux_defs ? 0 : c; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - if (imux) - snd_hda_codec_write(codec, spec->adc_nids[c], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - } + if (!nid) + return 0; + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec); +#define alc_auto_add_stereo_vol(codec, pfx, cidx, nid) \ + alc_auto_add_vol_ctl(codec, pfx, cidx, nid, 3) -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ -static int alc880_parse_auto_config(struct hda_codec *codec) +static int alc_auto_add_sw_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) +{ + int wid_type; + int type; + unsigned long val; + if (!nid) + return 0; + wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); + } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); + } else { + type = ALC_CTL_BIND_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); + } + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); +} + +#define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \ + alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3) + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_mute(codec, pin, HDA_OUTPUT)) + return pin; + else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) + return mix; + else if (nid_has_mute(codec, dac, HDA_OUTPUT)) + return dac; + return 0; +} + +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 mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_volume(codec, dac, HDA_OUTPUT)) + return dac; + else if (nid_has_volume(codec, mix, HDA_OUTPUT)) + return mix; + else if (nid_has_volume(codec, pin, HDA_OUTPUT)) + return pin; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct alc_spec *spec = codec->spec; + int i, err, noutputs; + + noutputs = cfg->line_outs; + if (spec->multi_ios > 0) + noutputs += spec->multi_ios; + + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + hda_nid_t dac, pin; + hda_nid_t sw, vol; + + dac = spec->multiout.dac_nids[i]; + if (!dac) + continue; + if (i >= cfg->line_outs) + pin = spec->multi_io[i - 1].pin; + else + pin = cfg->line_out_pins[i]; + + 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) { + /* Center/LFE */ + err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); + if (err < 0) + return err; + err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); + if (err < 0) + return err; + } else { + err = alc_auto_add_stereo_vol(codec, name, index, vol); + if (err < 0) + return err; + err = alc_auto_add_stereo_sw(codec, name, index, sw); + if (err < 0) + return err; + } + } + return 0; +} + +/* add playback controls for speaker and HP outputs */ +static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac, const char *pfx) { struct alc_spec *spec = codec->spec; + hda_nid_t sw, vol; int err; - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc880_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ + if (!pin) + return 0; + if (!dac) { + /* the corresponding DAC is already occupied */ + if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) + return 0; /* no way */ + /* create a switch only */ + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + } - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + sw = alc_look_for_out_mute_nid(codec, pin, dac); + vol = alc_look_for_out_vol_nid(codec, pin, dac); + err = alc_auto_add_stereo_vol(codec, pfx, 0, vol); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); - if (err < 0) - return err; - err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], - "Headphone"); - if (err < 0) - return err; - err = alc880_auto_create_input_ctls(codec, &spec->autocfg); + err = alc_auto_add_stereo_sw(codec, pfx, 0, sw); if (err < 0) return err; + return 0; +} - spec->multiout.max_channels = spec->multiout.num_dacs * 2; +static int alc_auto_create_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + spec->multiout.hp_nid, + "Headphone"); +} - alc_auto_parse_digital(codec); +static int alc_auto_create_speaker_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], + spec->multiout.extra_out_nid[0], + "Speaker"); +} - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac) +{ + int i, num; + hda_nid_t nid, mix = 0; + hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - add_verb(spec, alc880_volume_init_verbs); + alc_set_pin_output(codec, pin, pin_type); + nid = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) + continue; + mix = srcs[i]; + break; + } + if (!mix) + return; - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; + /* need the manual connection? */ + if (num > 1) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); + /* unmute mixer widget inputs */ + if (nid_has_mute(codec, mix, HDA_INPUT)) { + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } + /* initialize volume */ + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); +} + +static void alc_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int pin_type = get_pin_type(spec->autocfg.line_out_type); + int i; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc_auto_set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + } +} + +static void alc_auto_init_extra_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pins[0]; + if (pin) + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, + spec->multiout.hp_nid); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, + spec->multiout.extra_out_nid[0]); +} + +/* + * multi-io helper + */ +static int alc_auto_fill_multi_ios(struct hda_codec *codec, + unsigned int location) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int type, i, num_pins = 0; + + 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; + 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) + continue; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + continue; + dac = alc_auto_look_for_dac(codec, nid); + if (!dac) + 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->multiout.num_dacs = 1; + if (num_pins < 2) + return 0; + return num_pins; +} - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); +static int alc_auto_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; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->multi_ios + 1; + if (uinfo->value.enumerated.item > spec->multi_ios) + uinfo->value.enumerated.item = spec->multi_ios; + sprintf(uinfo->value.enumerated.name, "%dch", + (uinfo->value.enumerated.item + 1) * 2); + return 0; +} + +static int alc_auto_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; + ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + return 0; +} + +static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = spec->multi_io[idx].pin; + + if (!spec->multi_io[idx].ctl_in) + spec->multi_io[idx].ctl_in = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (output) { + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); + alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); + } else { + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->multi_io[idx].ctl_in); + } + return 0; +} + +static int alc_auto_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 i, ch; + + ch = ucontrol->value.enumerated.item[0]; + if (ch < 0 || ch > spec->multi_ios) + return -EINVAL; + if (ch == (spec->ext_channel_count - 1) / 2) + return 0; + spec->ext_channel_count = (ch + 1) * 2; + for (i = 0; i < spec->multi_ios; i++) + alc_set_multi_io(codec, i, i < ch); + spec->multiout.max_channels = spec->ext_channel_count; + if (spec->need_dac_fix && !spec->const_channel_count) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; return 1; } -/* additional initialization for auto-configuration model */ -static void alc880_auto_init(struct hda_codec *codec) +static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_auto_ch_mode_info, + .get = alc_auto_ch_mode_get, + .put = alc_auto_ch_mode_put, +}; + +static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, + int (*fill_dac)(struct hda_codec *)) { struct alc_spec *spec = codec->spec; - alc880_auto_init_multi_out(codec); - alc880_auto_init_extra_out(codec); - alc880_auto_init_analog_input(codec); - alc880_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int location, defcfg; + int num_pins; + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { + /* use HP as primary 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; + if (fill_dac) + fill_dac(codec); + } + if (cfg->line_outs != 1 || + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + return 0; + + 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); + if (num_pins > 0) { + struct snd_kcontrol_new *knew; + + knew = alc_kcontrol_new(spec); + if (!knew) + return -ENOMEM; + *knew = alc_auto_channel_mode_enum; + knew->name = kstrdup("Channel Mode", GFP_KERNEL); + if (!knew->name) + return -ENOMEM; + + spec->multi_ios = num_pins; + spec->ext_channel_count = 2; + spec->multiout.num_dacs = num_pins + 1; + } + return 0; } -/* check the ADC/MUX contains all input pins; some ADC/MUX contains only - * one of two digital mic pins, e.g. on ALC272 +/* filter out invalid adc_nids (and capsrc_nids) that don't give all + * active input pins */ -static void fixup_automic_adc(struct hda_codec *codec) +static void alc_remove_invalid_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i; + const struct hda_input_mux *imux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = spec->capsrc_nids ? - spec->capsrc_nids[i] : spec->adc_nids[i]; - int iidx, eidx; + imux = spec->input_mux; + if (!imux) + return; + if (spec->dyn_adc_switch) + return; - iidx = get_connection_index(codec, cap, spec->int_mic.pin); - if (iidx < 0) - continue; - eidx = get_connection_index(codec, cap, spec->ext_mic.pin); - if (eidx < 0) - continue; - spec->int_mic.mux_idx = iidx; - spec->ext_mic.mux_idx = eidx; - if (spec->capsrc_nids) - spec->capsrc_nids += i; - spec->adc_nids += i; - spec->num_adc_nids = 1; - /* optional dock-mic */ - eidx = get_connection_index(codec, cap, spec->dock_mic.pin); - if (eidx < 0) - spec->dock_mic.pin = 0; - else - spec->dock_mic.mux_idx = eidx; + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + hda_nid_t cap = spec->private_capsrc_nids[n]; + int num_conns = snd_hda_get_conn_list(codec, cap, NULL); + for (i = 0; i < imux->num_items; i++) { + hda_nid_t pin = spec->imux_pins[i]; + if (pin) { + if (get_connection_index(codec, cap, pin) < 0) + break; + } else if (num_conns <= imux->items[i].index) + break; + } + if (i >= imux->num_items) { + adc_nids[nums] = spec->private_adc_nids[n]; + capsrc_nids[nums++] = cap; + } + } + if (!nums) { + /* check whether ADC-switch is possible */ + if (!alc_check_dyn_adc_switch(codec)) { + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", + codec->chip_name, spec->private_adc_nids[0]); + spec->num_adc_nids = 1; + spec->auto_mic = 0; + return; + } + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + memcpy(spec->private_capsrc_nids, capsrc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (spec->auto_mic) + alc_auto_mic_check_imux(codec); /* check auto-mic setups */ + else if (spec->input_mux->num_items == 1) + spec->num_adc_nids = 1; /* reduce to a single ADC */ +} + +/* + * initialize ADC paths + */ +static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + + nid = spec->adc_nids[adc_idx]; + /* mute ADC */ + if (nid_has_mute(codec, nid, HDA_INPUT)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + return; + } + if (!spec->capsrc_nids) return; + nid = spec->capsrc_nids[adc_idx]; + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +} + +static void alc_auto_init_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int c, nums; + + for (c = 0; c < spec->num_adc_nids; c++) + alc_auto_init_adc(codec, c); + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + for (c = 0; c < nums; c++) + alc_mux_select(codec, 0, spec->cur_mux[c], true); +} + +/* add mic boosts if needed */ +static int alc_auto_add_mic_boost(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + int type_idx = 0; + hda_nid_t nid; + const char *prev_label = NULL; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + const char *label; + char boost_label[32]; + + 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(boost_label, sizeof(boost_label), + "%s Boost Volume", label); + err = add_control(spec, ALC_CTL_WIDGET_VOL, + boost_label, type_idx, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } } - snd_printd(KERN_INFO "hda_codec: %s: " - "No ADC/MUX containing both 0x%x and 0x%x pins\n", - codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin); - spec->auto_mic = 0; /* disable auto-mic to be sure */ + return 0; } /* select or unmute the given capsrc route */ @@ -5793,7 +3404,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, HDA_AMP_MUTE, 0); - } else { + } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { snd_hda_codec_write_cache(codec, cap, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -5821,46 +3432,17 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) return -1; /* not found */ } -/* choose the ADC/MUX containing the input pin and initialize the setup */ -static void fixup_single_adc(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - /* search for the input pin; there must be only one */ - if (cfg->num_inputs != 1) - return; - i = init_capsrc_for_pin(codec, cfg->inputs[0].pin); - if (i >= 0) { - /* use only this ADC */ - if (spec->capsrc_nids) - spec->capsrc_nids += i; - spec->adc_nids += i; - spec->num_adc_nids = 1; - spec->single_input_src = 1; - } -} - -/* initialize dual adcs */ -static void fixup_dual_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - init_capsrc_for_pin(codec, spec->ext_mic.pin); - init_capsrc_for_pin(codec, spec->dock_mic.pin); - init_capsrc_for_pin(codec, spec->int_mic.pin); -} - /* initialize some special cases for input sources */ static void alc_init_special_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->dual_adc_switch) - fixup_dual_adc_switch(codec); - else if (spec->single_input_src) - init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin); + int i; + + for (i = 0; i < spec->autocfg.num_inputs; i++) + init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); } +/* assign appropriate capture mixers */ static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5872,86 +3454,56 @@ static void set_capture_mixer(struct hda_codec *codec) alc_capture_mixer2, alc_capture_mixer3 }, }; - if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { + + /* check whether either of ADC or MUX has a volume control */ + if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { + if (!spec->capsrc_nids) + return; /* no volume */ + if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) + return; /* no volume in capsrc, too */ + spec->vol_in_capsrc = 1; + } + + if (spec->num_adc_nids > 0) { int mux = 0; - int num_adcs = spec->num_adc_nids; - if (spec->dual_adc_switch) + int num_adcs = 0; + + if (spec->input_mux && spec->input_mux->num_items > 1) + mux = 1; + if (spec->auto_mic) { num_adcs = 1; - else if (spec->auto_mic) - fixup_automic_adc(codec); - else if (spec->input_mux) { - if (spec->input_mux->num_items > 1) - mux = 1; - else if (spec->input_mux->num_items == 1) - fixup_single_adc(codec); + mux = 0; + } else if (spec->dyn_adc_switch) + num_adcs = 1; + if (!num_adcs) { + if (spec->num_adc_nids > 3) + spec->num_adc_nids = 3; + else if (!spec->num_adc_nids) + return; + num_adcs = spec->num_adc_nids; } spec->cap_mixer = caps[mux][num_adcs - 1]; } } -/* fill adc_nids (and capsrc_nids) containing all active input pins */ -static void fillup_priv_adc_nids(struct hda_codec *codec, const hda_nid_t *nids, - int num_nids) +/* + * standard auto-parser initializations + */ +static void alc_auto_init_std(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int n; - hda_nid_t fallback_adc = 0, fallback_cap = 0; - - for (n = 0; n < num_nids; n++) { - hda_nid_t adc, cap; - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int nconns, i, j; - - adc = nids[n]; - if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN) - continue; - cap = adc; - nconns = snd_hda_get_connections(codec, cap, conn, - ARRAY_SIZE(conn)); - if (nconns == 1) { - cap = conn[0]; - nconns = snd_hda_get_connections(codec, cap, conn, - ARRAY_SIZE(conn)); - } - if (nconns <= 0) - continue; - if (!fallback_adc) { - fallback_adc = adc; - fallback_cap = cap; - } - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - for (j = 0; j < nconns; j++) { - if (conn[j] == nid) - break; - } - if (j >= nconns) - break; - } - if (i >= cfg->num_inputs) { - int num_adcs = spec->num_adc_nids; - spec->private_adc_nids[num_adcs] = adc; - spec->private_capsrc_nids[num_adcs] = cap; - spec->num_adc_nids++; - spec->adc_nids = spec->private_adc_nids; - if (adc != cap) - spec->capsrc_nids = spec->private_capsrc_nids; - } - } - if (!spec->num_adc_nids) { - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, fallback_adc); - spec->private_adc_nids[0] = fallback_adc; - spec->adc_nids = spec->private_adc_nids; - if (fallback_adc != fallback_cap) { - spec->private_capsrc_nids[0] = fallback_cap; - spec->capsrc_nids = spec->private_adc_nids; - } - } + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + if (spec->unsol_event) + alc_inithook(codec); } +/* + * Digital-beep handlers + */ #ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) @@ -5979,1402 +3531,195 @@ static inline int has_cdefine_beep(struct hda_codec *codec) #define has_cdefine_beep(codec) 0 #endif -/* - * OK, here we have finally the patch for ALC880 +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code */ - -static int patch_alc880(struct hda_codec *codec) +static int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids) { - struct alc_spec *spec; - int board_config; + struct alc_spec *spec = codec->spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - board_config = snd_hda_check_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 = ALC880_AUTO; - } - - if (board_config == ALC880_AUTO) { - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) { - alc_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO - "hda_codec: Cannot set up configuration " - "from BIOS. Using 3-stack mode...\n"); - board_config = ALC880_3ST; - } - } - - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + ignore_nids); + if (err < 0) return err; - } - - if (board_config != ALC880_AUTO) - setup_preset(codec, &alc880_presets[board_config]); - - spec->stream_analog_playback = &alc880_pcm_analog_playback; - spec->stream_analog_capture = &alc880_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; - - spec->stream_digital_playback = &alc880_pcm_digital_playback; - spec->stream_digital_capture = &alc880_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]); - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc880_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); - } else { - spec->adc_nids = alc880_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); + if (!spec->autocfg.line_outs) { + if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { + spec->multiout.max_channels = 2; + spec->no_analog = 1; + goto dig_only; } + return 0; /* can't find valid BIOS pin config */ } - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - - spec->vmaster_nid = 0x0c; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC880_AUTO) - spec->init_hook = alc880_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc880_loopbacks; -#endif - - return 0; -} - - -/* - * ALC260 support - */ - -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 }, -}; + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); + if (err < 0) + return err; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; -/* Mixer combinations - * - * basic: base_output + input + pc_beep + capture - * HP: base_output + input + capture_alt - * HP_3013: hp_3013 + input + capture - * fujitsu: fujitsu + capture - * acer: acer + capture - */ + dig_only: + alc_auto_parse_digital(codec); -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 */ -}; + if (!spec->no_analog) + alc_remove_invalid_adc_nids(codec); -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 */ -}; + if (ssid_nids) + alc_ssid_check(codec, ssid_nids); -/* update HP, line and mono out pins according to the master switch */ -static void alc260_hp_master_update(struct hda_codec *codec) -{ - update_speakers(codec); -} - -static int alc260_hp_master_sw_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; - *ucontrol->value.integer.value = !spec->master_mute; - return 0; -} + if (!spec->no_analog) { + alc_auto_check_switches(codec); + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + } -static int alc260_hp_master_sw_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 val = !*ucontrol->value.integer.value; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - if (val == spec->master_mute) - return 0; - spec->master_mute = val; - alc260_hp_master_update(codec); return 1; } -static const struct snd_kcontrol_new alc260_hp_output_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, - .info = snd_ctl_boolean_mono_info, - .get = alc260_hp_master_sw_get, - .put = alc260_hp_master_sw_put, - }, - 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("Speaker Playback Volume", 0x0a, 1, 0x0, - HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc260_hp_unsol_verbs[] = { - {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {}, -}; - -static void alc260_hp_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x0f; - spec->autocfg.speaker_pins[0] = 0x10; - spec->autocfg.speaker_pins[1] = 0x11; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct snd_kcontrol_new alc260_hp_3013_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, - .info = snd_ctl_boolean_mono_info, - .get = alc260_hp_master_sw_get, - .put = alc260_hp_master_sw_put, - }, - HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static void alc260_hp_3013_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x10; - spec->autocfg.speaker_pins[1] = 0x11; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct hda_bind_ctls alc260_dc7600_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc260_dc7600_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol), - HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb alc260_hp_3013_unsol_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {}, -}; - -static void alc260_hp_3012_setup(struct hda_codec *codec) +static int alc880_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x10; - spec->autocfg.speaker_pins[0] = 0x0f; - spec->autocfg.speaker_pins[1] = 0x11; - spec->autocfg.speaker_pins[2] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; + static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); } -/* 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), +#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 */ }; - -/* - * 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)}, - { } -}; - -#if 0 /* should be identical with alc260_init_verbs? */ -static const struct hda_verb alc260_hp_init_verbs[] = { - /* Headphone and output */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - /* mono output */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Line-2 pin widget for output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* unmute amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute Line-Out mixer amp left and right (volume = 0) */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute HP mixer amp left and right (volume = 0) */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* 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 */ - /* Unmute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - { } -}; #endif -static const struct hda_verb alc260_hp_3013_init_verbs[] = { - /* Line out and output */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* mono output */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic1 (rear panel) pin widget for input and vref at 80% */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Mic2 (front panel) pin widget for input and vref at 80% */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - /* Line In pin widget for input */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* Headphone pin widget for output */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - /* CD pin widget for input */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - /* unmute amp left and right */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000}, - /* set connection select to line in (default select for this ADC) */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* unmute Line-Out mixer amp left and right (volume = 0) */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* unmute HP mixer amp left and right (volume = 0) */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* mute pin widget amp left and right (no gain on this amp) */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - /* 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 */ - /* Unmute Front out path */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Headphone out path */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - /* Unmute Mono out path */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - { } -}; - -/* 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 | ALC880_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) == ALC880_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 | ALC880_HP_EVENT}, - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_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. +/* + * board setups */ -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 */ - - { } -}; +#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 -#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback -#define alc260_pcm_analog_capture alc880_pcm_analog_capture - -#define alc260_pcm_digital_playback alc880_pcm_digital_playback -#define alc260_pcm_digital_capture alc880_pcm_digital_capture - /* - * for BIOS auto-configuration + * OK, here we have finally the patch for ALC880 */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc880_quirks.c" +#endif -static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vol_bits) +static int patch_alc880(struct hda_codec *codec) { - hda_nid_t nid_vol; - unsigned long vol_val, sw_val; + struct alc_spec *spec; + int board_config; int err; - if (nid >= 0x0f && nid < 0x11) { - nid_vol = nid - 0x7; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - } else if (nid == 0x11) { - nid_vol = nid - 0x7; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); - } else if (nid >= 0x12 && nid <= 0x15) { - nid_vol = 0x08; - vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT); - sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - } else - return 0; /* N/A */ - - if (!(*vol_bits & (1 << nid_vol))) { - /* first control for the volume widget */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); - if (err < 0) - return err; - *vol_bits |= (1 << nid_vol); - } - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); - if (err < 0) - return err; - return 1; -} + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; -/* add playback controls from the parsed DAC table */ -static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int err; - int vols = 0; + codec->spec = spec; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 0x02; - - nid = cfg->line_out_pins[0]; - if (nid) { - const char *pfx; - if (!cfg->speaker_pins[0] && !cfg->hp_pins[0]) - pfx = "Master"; - else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - pfx = "Speaker"; - else - pfx = "Front"; - err = alc260_add_playback_controls(spec, nid, pfx, &vols); - if (err < 0) - return err; - } + spec->mixer_nid = 0x0b; + spec->need_dac_fix = 1; - nid = cfg->speaker_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Speaker", &vols); - if (err < 0) - return err; + 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; } - nid = cfg->hp_pins[0]; - if (nid) { - err = alc260_add_playback_controls(spec, nid, "Headphone", - &vols); - if (err < 0) + if (board_config == ALC_MODEL_AUTO) { + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); return err; + } +#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 } - return 0; -} -/* create playback/capture controls for input pins */ -static int alc260_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05); -} + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc880_presets[board_config]); -static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int sel_idx) -{ - alc_set_pin_output(codec, nid, pin_type); - /* need the manual connection? */ - if (nid >= 0x12) { - int idx = nid - 0x12; - snd_hda_codec_write(codec, idx + 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, sel_idx); + 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); } -} -static void alc260_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); - nid = spec->autocfg.line_out_pins[0]; - if (nid) { - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0); + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - nid = spec->autocfg.speaker_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); - - nid = spec->autocfg.hp_pins[0]; - if (nid) - alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); -} + spec->vmaster_nid = 0x0c; -#define ALC260_PIN_CD_NID 0x16 -static void alc260_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; + codec->patch_ops = alc_patch_ops; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc880_loopbacks; +#endif - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (nid >= 0x12) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC260_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } + return 0; } -#define alc260_auto_init_input_src alc880_auto_init_input_src /* - * generic initialization of ADC, input mixers and output mixers + * ALC260 support */ -static const struct hda_verb alc260_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x05, 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 */ - /* 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)}, - - /* - * Set up output mixers (0x08 - 0x0a) - */ - /* set vol=0 to output mixers */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - { } -}; - static int alc260_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc260_ignore); - if (err < 0) - return err; - err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->kctls.list) - return 0; /* can't find valid BIOS pin config */ - err = alc260_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc260_volume_init_verbs); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); - - return 1; -} - -/* additional initialization for auto-configuration model */ -static void alc260_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc260_auto_init_multi_out(codec); - alc260_auto_init_analog_input(codec); - alc260_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; + return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -7411,186 +3756,10 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { }; /* - * ALC260 configurations */ -static const char * const alc260_models[ALC260_MODEL_LAST] = { - [ALC260_BASIC] = "basic", - [ALC260_HP] = "hp", - [ALC260_HP_3013] = "hp-3013", - [ALC260_HP_DC7600] = "hp-dc7600", - [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(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */ - SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600), - SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013), - SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP), - SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), - SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP), - 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_HP] = { - .mixers = { alc260_hp_output_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs, - alc260_hp_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_setup, - .init_hook = alc_inithook, - }, - [ALC260_HP_DC7600] = { - .mixers = { alc260_hp_dc7600_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_init_verbs, - alc260_hp_dc7600_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_3012_setup, - .init_hook = alc_inithook, - }, - [ALC260_HP_3013] = { - .mixers = { alc260_hp_3013_mixer, - alc260_input_mixer }, - .init_verbs = { alc260_hp_3013_init_verbs, - alc260_hp_3013_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc260_dac_nids), - .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), - .adc_nids = alc260_adc_nids_alt, - .num_channel_mode = ARRAY_SIZE(alc260_modes), - .channel_mode = alc260_modes, - .input_mux = &alc260_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc260_hp_3013_setup, - .init_hook = alc_inithook, - }, - [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, - }, +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc260_quirks.c" #endif -}; static int patch_alc260(struct hda_codec *codec) { @@ -7603,73 +3772,66 @@ static int patch_alc260(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST, - alc260_models, - alc260_cfg_tbl); + 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 = ALC260_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC260_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 == ALC260_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc260_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 } - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc260_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); } - if (board_config != ALC260_AUTO) - setup_preset(codec, &alc260_presets[board_config]); + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); - spec->stream_analog_playback = &alc260_pcm_analog_playback; - spec->stream_analog_capture = &alc260_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc260_pcm_analog_capture; - - spec->stream_digital_playback = &alc260_pcm_digital_playback; - spec->stream_digital_capture = &alc260_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x04 is valid */ - unsigned int wcap = get_wcaps(codec, 0x04); - wcap = get_wcaps_type(wcap); - /* get type */ - if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc260_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); - } else { - spec->adc_nids = alc260_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; } + set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); } - set_capture_mixer(codec); - set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); spec->vmaster_nid = 0x08; codec->patch_ops = alc_patch_ops; - if (board_config == ALC260_AUTO) - spec->init_hook = alc260_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) @@ -7691,3299 +3853,10 @@ static int patch_alc260(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#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 alc883_adc_nids_alt[1] = { 0x08 }; -static const hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 }; -#define alc889_adc_nids alc880_adc_nids - -static const hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; -static const hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; -#define alc883_capsrc_nids alc882_capsrc_nids_alt -static const hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; -#define alc889_capsrc_nids alc882_capsrc_nids - -/* 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 alc889_capture_source = { - .num_items = 3, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x3 }, - { "Line", 0x2 }, - }, -}; - -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 alc883_lenovo_101e_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc883_lenovo_nb0763_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_input_mux alc883_lenovo_sky_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x4 }, - }, -}; - -static const struct hda_input_mux alc883_asus_eee1601_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - }, -}; - -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! */ - }, -}; - -/* - * 2ch mode - */ -static const struct hda_channel_mode alc883_3ST_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc882_3ST_ch2_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, 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 }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc882_3ST_ch4_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, 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 }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc882_3ST_ch6_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc882_3ST_6ch_modes[3] = { - { 2, alc882_3ST_ch2_init }, - { 4, alc882_3ST_ch4_init }, - { 6, alc882_3ST_ch6_init }, -}; - -#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes - -/* - * 2ch mode - */ -static const struct hda_verb alc883_3ST_ch2_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, 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 }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_3ST_ch4_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, 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 }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_3ST_ch6_clevo_init[] = { - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = { - { 2, alc883_3ST_ch2_clevo_init }, - { 4, alc883_3ST_ch4_clevo_init }, - { 6, alc883_3ST_ch6_clevo_init }, -}; - - -/* - * 6ch mode - */ -static const struct hda_verb alc882_sixstack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, 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 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc882_sixstack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc882_sixstack_modes[2] = { - { 6, alc882_sixstack_ch6_init }, - { 8, alc882_sixstack_ch8_init }, -}; - - -/* 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 - -/* - * 2ch mode - */ -static const struct hda_verb alc883_4ST_ch2_init[] = { - { 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 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_4ST_ch4_init[] = { - { 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 }, - { 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 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_4ST_ch6_init[] = { - { 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_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc883_4ST_ch8_init[] = { - { 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 }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_4ST_8ch_modes[4] = { - { 2, alc883_4ST_ch2_init }, - { 4, alc883_4ST_ch4_init }, - { 6, alc883_4ST_ch6_init }, - { 8, alc883_4ST_ch8_init }, -}; - - -/* - * 2ch mode - */ -static const struct hda_verb alc883_3ST_ch2_intel_init[] = { - { 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 }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc883_3ST_ch4_intel_init[] = { - { 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_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_3ST_ch6_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { - { 2, alc883_3ST_ch2_intel_init }, - { 4, alc883_3ST_ch4_intel_init }, - { 6, alc883_3ST_ch6_intel_init }, -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc889_ch2_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc889_ch6_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 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 alc889_ch8_intel_init[] = { - { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 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 alc889_8ch_intel_modes[3] = { - { 2, alc889_ch2_intel_init }, - { 6, alc889_ch6_intel_init }, - { 8, alc889_ch8_intel_init }, -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc883_sixstack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, 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 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc883_sixstack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc883_sixstack_modes[2] = { - { 6, alc883_sixstack_ch6_init }, - { 8, alc883_sixstack_ch8_init }, -}; - - -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ -static const struct snd_kcontrol_new alc882_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_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 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 */ -}; - -/* 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_w2jc_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc882_targa_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ??? - * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c - */ -static const struct snd_kcontrol_new alc882_asus_a7j_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 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("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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), - { } /* 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}, - - { } -}; - -static const struct hda_verb alc882_adc1_init_verbs[] = { - /* 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)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - { } -}; - -static const struct hda_verb alc882_eapd_verbs[] = { - /* change to EAPD mode */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3060}, - { } -}; - -static const struct hda_verb alc889_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc_hp15_unsol_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc885_init_verbs[] = { - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Front HP Pin: output 0 (0x0c) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* 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) */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x19, 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% */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, 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}, - - /* Mixer elements: 0x18, , 0x1a, 0x1b */ - /* Input mixer1 */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* 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)}, - /* ADC3: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - { } -}; - -static const struct hda_verb alc885_init_input_verbs[] = { - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - { } -}; - - -/* Unmute Selector 24h and set the default input to front mic */ -static const struct hda_verb alc889_init_input_verbs[] = { - {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - { } -}; - - -#define alc883_init_verbs alc882_base_init_verbs - -/* Mac Pro test */ -static const struct snd_kcontrol_new alc882_macpro_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - /* FIXME: this looks suspicious... - HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), - */ - { } /* end */ -}; - -static const struct hda_verb alc882_macpro_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)}, - /* Front Pin: output 0 (0x0c) */ - {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, 0x00}, - /* 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}, - /* Speaker: output */ - {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, 0x04}, - /* Headphone output (output 0 - 0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* 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}, - - { } -}; - -/* 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, ALC880_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, ALC880_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, (ALC880_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, ALC880_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, (ALC880_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}, - { } -}; - -/* iMac 24 mixer. */ -static const struct snd_kcontrol_new alc885_imac24_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), - { } /* end */ -}; - -/* iMac 24 init verbs. */ -static const struct hda_verb alc885_imac24_init_verbs[] = { - /* Internal speakers: output 0 (0x0c) */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Internal speakers: output 0 (0x0c) */ - {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, 0x00}, - /* Headphone: output 0 (0x0c) */ - {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, ALC880_HP_EVENT | AC_USRSP_EN}, - /* Front Mic: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } -}; - -/* 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; - spec->automute = 1; - spec->automute_mode = 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; - spec->automute = 1; - spec->automute_mode = 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; - spec->automute = 1; - spec->automute_mode = 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; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc882_targa_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc882_targa_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_hp_automute(codec); - snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, - spec->jack_present ? 1 : 3); -} - -static void alc882_targa_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc882_targa_automute(codec); -} - -static const struct hda_verb alc882_asus_a7j_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - { } /* end */ -}; - -static const struct hda_verb alc882_asus_a7m_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ - - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ - { } /* end */ -}; - -static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); - - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); - - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); - - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); - - msleep(1); - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); -} - -/* set up GPIO at initialization */ -static void alc885_macpro_init_hook(struct hda_codec *codec) -{ - alc882_gpio_mute(codec, 0, 0); - alc882_gpio_mute(codec, 1, 0); -} - -/* set up GPIO and update auto-muting at initialization */ -static void alc885_imac24_init_hook(struct hda_codec *codec) -{ - alc885_macpro_init_hook(codec); - alc_hp_automute(codec); -} - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc883_auto_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {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)}, - - /* - * 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_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)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* 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)}, - { } -}; - -/* 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 hda_verb alc883_medion_eapd_verbs[] = { - /* eanable EAPD on medion laptop */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - { } -}; - -#define alc883_base_mixer alc882_base_mixer - -static const struct snd_kcontrol_new alc883_mitac_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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", 0x15, 0x0, HDA_OUTPUT), - 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 alc883_clevo_m720_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_VOLUME("Mic Boost Volume", 0x18, 0, 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_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_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_VOLUME("Mic Boost Volume", 0x18, 0, 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_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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 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 alc883_3ST_6ch_intel_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", 0x15, 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, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc885_8ch_intel_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("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, 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, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x1b, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_fivestack_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 alc883_targa_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - 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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker 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("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("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_targa_8ch_mixer[] = { - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front 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_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), - 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("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 alc883_medion_wim2160_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc883_medion_wim2160_verbs[] = { - /* Unmute front mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Set speaker pin to front mixer */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* Init headphone pin */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_medion_wim2160_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1a; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, 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("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_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc888_lenovo_sky_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", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 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("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_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_vaiott_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_bind_ctls alc883_bind_cap_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc883_bind_cap_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { - HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), - HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* 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 */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_mitac_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc883_mitac_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Subwoofer */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */ - - { } /* end */ -}; - -static const struct hda_verb alc883_clevo_m540r_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Int speaker */ - /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/ - - /* enable unsolicited event */ - /* - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - */ - - { } /* end */ -}; - -static const struct hda_verb alc883_clevo_m720_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Int speaker */ - {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { - /* HP */ - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Subwoofer */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_targa_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - -/* Connect Line-Out side jack (SPDIF) to Side */ - {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}, -/* Connect Mic jack to CLFE */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, -/* Connect Line-in jack to Surround */ - {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}, -/* Connect HP out jack to Front */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static const struct hda_verb alc883_lenovo_101e_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc883_lenovo_nb0763_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - { } /* end */ -}; - -static const struct hda_verb alc888_lenovo_ms7195_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc883_haier_w66_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - { } /* end */ -}; - -static const struct hda_verb alc888_lenovo_sky_verbs[] = { - {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)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static const struct hda_verb alc888_6st_dell_verbs[] = { - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static const struct hda_verb alc883_vaiott_verbs[] = { - /* HP */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - /* enable unsolicited event */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } /* end */ -}; - -static void alc888_3st_hp_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->autocfg.speaker_pins[2] = 0x18; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_3st_hp_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc888_3st_hp_2ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static const struct hda_verb alc888_3st_hp_4ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 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, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc888_3st_hp_6ch_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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, 0x01 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc888_3st_hp_modes[3] = { - { 2, alc888_3st_hp_2ch_init }, - { 4, alc888_3st_hp_4ch_init }, - { 6, alc888_3st_hp_6ch_init }, -}; - -static void alc888_lenovo_ms7195_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_lenovo_nb0763_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -#define alc883_targa_init_hook alc882_targa_init_hook -#define alc883_targa_unsol_event alc882_targa_unsol_event - -static void alc883_clevo_m720_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_clevo_m720_init_hook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); -} - -static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_haier_w66_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_lenovo_101e_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_acer_aspire_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[1] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc883_acer_eapd_verbs[] = { - /* HP Pin: output 0 (0x0c) */ - {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}, - /* Front Pin: output 0 (0x0c) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* eanable EAPD on medion laptop */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static void alc888_6st_dell_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->autocfg.speaker_pins[3] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc888_lenovo_sky_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->autocfg.speaker_pins[3] = 0x17; - spec->autocfg.speaker_pins[4] = 0x1a; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc883_vaiott_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_asus_m90v_verbs[] = { - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* enable unsolicited event */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static void alc883_mode2_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.speaker_pins[2] = 0x16; - spec->ext_mic.pin = 0x18; - spec->int_mic.pin = 0x19; - spec->ext_mic.mux_idx = 0; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct hda_verb alc888_asus_eee1601_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0838}, - /* enable unsolicited event */ - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -static void alc883_eee1601_inithook(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - alc_hp_automute(codec); -} - -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, ALC880_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) == ALC880_HP_EVENT) - alc889A_mb31_automute(codec); -} - - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc882_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc882_pcm_analog_playback alc880_pcm_analog_playback -#define alc882_pcm_analog_capture alc880_pcm_analog_capture -#define alc882_pcm_digital_playback alc880_pcm_digital_playback -#define alc882_pcm_digital_capture alc880_pcm_digital_capture - -static const hda_nid_t alc883_slave_dig_outs[] = { - ALC1200_DIGOUT_NID, 0, -}; - -static const hda_nid_t alc1200_slave_dig_outs[] = { - ALC883_DIGOUT_NID, 0, -}; - -/* - * configuration and preset - */ -static const char * const alc882_models[ALC882_MODEL_LAST] = { - [ALC882_3ST_DIG] = "3stack-dig", - [ALC882_6ST_DIG] = "6stack-dig", - [ALC882_ARIMA] = "arima", - [ALC882_W2JC] = "w2jc", - [ALC882_TARGA] = "targa", - [ALC882_ASUS_A7J] = "asus-a7j", - [ALC882_ASUS_A7M] = "asus-a7m", - [ALC885_MACPRO] = "macpro", - [ALC885_MB5] = "mb5", - [ALC885_MACMINI3] = "macmini3", - [ALC885_MBA21] = "mba21", - [ALC885_MBP3] = "mbp3", - [ALC885_IMAC24] = "imac24", - [ALC885_IMAC91] = "imac91", - [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", - [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", - [ALC883_3ST_6ch] = "3stack-6ch", - [ALC883_6ST_DIG] = "alc883-6stack-dig", - [ALC883_TARGA_DIG] = "targa-dig", - [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", - [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", - [ALC883_ACER] = "acer", - [ALC883_ACER_ASPIRE] = "acer-aspire", - [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", - [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g", - [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", - [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", - [ALC883_MEDION] = "medion", - [ALC883_MEDION_WIM2160] = "medion-wim2160", - [ALC883_LAPTOP_EAPD] = "laptop-eapd", - [ALC883_LENOVO_101E_2ch] = "lenovo-101e", - [ALC883_LENOVO_NB0763] = "lenovo-nb0763", - [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", - [ALC888_LENOVO_SKY] = "lenovo-sky", - [ALC883_HAIER_W66] = "haier-w66", - [ALC888_3ST_HP] = "3stack-hp", - [ALC888_6ST_DELL] = "6stack-dell", - [ALC883_MITAC] = "mitac", - [ALC883_CLEVO_M540R] = "clevo-m540r", - [ALC883_CLEVO_M720] = "clevo-m720", - [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", - [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", - [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", - [ALC889A_INTEL] = "intel-alc889a", - [ALC889_INTEL] = "intel-x58", - [ALC1200_ASUS_P5Q] = "asus-p5q", - [ALC889A_MB31] = "mb31", - [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", - [ALC882_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc882_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), - - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC888_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC888_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC888_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO), - SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC888_ACER_ASPIRE_6530G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC888_ACER_ASPIRE_6530G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC888_ACER_ASPIRE_7730G), - /* default Acer -- disabled as it causes more problems. - * model=auto should work fine now - */ - /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */ - - SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), - - SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavilion", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), - - SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), - SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), - SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), - SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), - - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), - SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), - SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), - SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), - - SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ - SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), - - SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), - SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), - SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), - /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */ - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), - SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", - ALC883_FUJITSU_PI2515), - SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx", - ALC888_FUJITSU_XA3530), - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), - SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY), - SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), - SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), - - SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), - SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), - SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), - SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), - SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), - SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG), - - {} -}; - -/* 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, 0x0c00, "Mac Pro", ALC885_MACPRO), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M), - 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, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), - 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[] = { - [ALC882_3ST_DIG] = { - .mixers = { alc882_base_mixer }, - .init_verbs = { alc882_base_init_verbs, - alc882_adc1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_6ST_DIG] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, - alc882_adc1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_ARIMA] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_W2JC] = { - .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs, alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - }, - [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 = alc_sku_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 = alc_sku_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 = alc_sku_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 = alc_sku_unsol_event, - .setup = alc885_macmini3_setup, - .init_hook = alc_hp_automute, - }, - [ALC885_MACPRO] = { - .mixers = { alc882_macpro_mixer }, - .init_verbs = { alc882_macpro_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .init_hook = alc885_macpro_init_hook, - }, - [ALC885_IMAC24] = { - .mixers = { alc885_imac24_mixer }, - .init_verbs = { alc885_imac24_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc885_imac24_setup, - .init_hook = alc885_imac24_init_hook, - }, - [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 = alc_sku_unsol_event, - .setup = alc885_imac91_setup, - .init_hook = alc_hp_automute, - }, - [ALC882_TARGA] = { - .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc880_gpio3_init_verbs, alc882_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC882_ASUS_A7J] = { - .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_asus_a7j_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_ASUS_A7M] = { - .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, - alc882_eapd_verbs, alc880_gpio1_init_verbs, - alc882_asus_a7m_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC883_3ST_2ch_DIG] = { - .mixers = { alc883_3ST_2ch_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch_DIG] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - }, - [ALC883_3ST_6ch_INTEL] = { - .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), - .channel_mode = alc883_3ST_6ch_intel_modes, - .need_dac_fix = 1, - .input_mux = &alc883_3stack_6ch_intel, - }, - [ALC889A_INTEL] = { - .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc885_init_verbs, alc885_init_input_verbs, - alc_hp15_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), - .channel_mode = alc889_8ch_intel_modes, - .capsrc_nids = alc889_capsrc_nids, - .input_mux = &alc889_capture_source, - .setup = alc889_automute_setup, - .init_hook = alc_hp_automute, - .unsol_event = alc_sku_unsol_event, - .need_dac_fix = 1, - }, - [ALC889_INTEL] = { - .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, - .init_verbs = { alc885_init_verbs, alc889_init_input_verbs, - alc889_eapd_verbs, alc_hp15_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc883_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), - .channel_mode = alc889_8ch_intel_modes, - .capsrc_nids = alc889_capsrc_nids, - .input_mux = &alc889_capture_source, - .setup = alc889_automute_setup, - .init_hook = alc889_intel_init_hook, - .unsol_event = alc_sku_unsol_event, - .need_dac_fix = 1, - }, - [ALC883_6ST_DIG] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_TARGA_DIG] = { - .mixers = { alc883_targa_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_TARGA_2ch_DIG] = { - .mixers = { alc883_targa_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_TARGA_8ch_DIG] = { - .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, - alc883_targa_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes), - .channel_mode = alc883_4ST_8ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_targa_unsol_event, - .setup = alc882_targa_setup, - .init_hook = alc882_targa_automute, - }, - [ALC883_ACER] = { - .mixers = { alc883_base_mixer }, - /* 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. - */ - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_ACER_ASPIRE] = { - .mixers = { alc883_acer_aspire_mixer }, - .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_acer_aspire_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_4930G] = { - .mixers = { alc888_acer_aspire_4930g_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_4930g_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_2_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_4930g_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_6530G] = { - .mixers = { alc888_acer_aspire_6530_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_6530g_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_acer_aspire_6530_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_6530g_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ACER_ASPIRE_8930G] = { - .mixers = { alc889_acer_aspire_8930g_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc889_acer_aspire_8930g_verbs, - alc889_eapd_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), - .adc_nids = alc889_adc_nids, - .capsrc_nids = alc889_capsrc_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .num_mux_defs = - ARRAY_SIZE(alc889_capture_sources), - .input_mux = alc889_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc889_acer_aspire_8930g_setup, - .init_hook = alc_hp_automute, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .power_hook = alc_power_eapd, -#endif - }, - [ALC888_ACER_ASPIRE_7730G] = { - .mixers = { alc883_3ST_6ch_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_7730G_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .const_channel_count = 6, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_acer_aspire_7730g_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_MEDION] = { - .mixers = { alc883_fivestack_mixer, - alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, - alc883_medion_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_MEDION_WIM2160] = { - .mixers = { alc883_medion_wim2160_mixer }, - .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_medion_wim2160_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_LAPTOP_EAPD] = { - .mixers = { alc883_base_mixer }, - .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - }, - [ALC883_CLEVO_M540R] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes), - .channel_mode = alc883_3ST_6ch_clevo_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - /* This machine has the hardware HP auto-muting, thus - * we need no software mute via unsol event - */ - }, - [ALC883_CLEVO_M720] = { - .mixers = { alc883_clevo_m720_mixer }, - .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc883_clevo_m720_unsol_event, - .setup = alc883_clevo_m720_setup, - .init_hook = alc883_clevo_m720_init_hook, - }, - [ALC883_LENOVO_101E_2ch] = { - .mixers = { alc883_lenovo_101e_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .adc_nids = alc883_adc_nids_alt, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), - .capsrc_nids = alc883_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_lenovo_101e_capture_source, - .setup = alc883_lenovo_101e_setup, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc_inithook, - }, - [ALC883_LENOVO_NB0763] = { - .mixers = { alc883_lenovo_nb0763_mixer }, - .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_lenovo_nb0763_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_lenovo_nb0763_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_LENOVO_MS7195_DIG] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_lenovo_ms7195_setup, - .init_hook = alc_inithook, - }, - [ALC883_HAIER_W66] = { - .mixers = { alc883_targa_2ch_mixer}, - .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_haier_w66_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_3ST_HP] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), - .channel_mode = alc888_3st_hp_modes, - .need_dac_fix = 1, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_3st_hp_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_6ST_DELL] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_6st_dell_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_MITAC] = { - .mixers = { alc883_mitac_mixer }, - .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_mitac_setup, - .init_hook = alc_hp_automute, - }, - [ALC883_FUJITSU_PI2515] = { - .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, - .init_verbs = { alc883_init_verbs, - alc883_2ch_fujitsu_pi2515_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_2ch_fujitsu_pi2515_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_FUJITSU_XA3530] = { - .mixers = { alc888_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, - alc888_fujitsu_xa3530_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), - .channel_mode = alc888_4ST_8ch_intel_modes, - .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_2_capture_sources, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_fujitsu_xa3530_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_LENOVO_SKY] = { - .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .need_dac_fix = 1, - .input_mux = &alc883_lenovo_sky_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc888_lenovo_sky_setup, - .init_hook = alc_hp_automute, - }, - [ALC888_ASUS_M90V] = { - .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), - .channel_mode = alc883_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_mode2_setup, - .init_hook = alc_inithook, - }, - [ALC888_ASUS_EEE1601] = { - .mixers = { alc883_asus_eee1601_mixer }, - .cap_mixer = alc883_asus_eee1601_cap_mixer, - .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .input_mux = &alc883_asus_eee1601_capture_source, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc883_eee1601_inithook, - }, - [ALC1200_ASUS_P5Q] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC1200_DIGOUT_NID, - .dig_in_nid = ALC883_DIGIN_NID, - .slave_dig_outs = alc1200_slave_dig_outs, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, - }, - [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, - }, - [ALC883_SONY_VAIO_TT] = { - .mixers = { alc883_vaiott_mixer }, - .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .input_mux = &alc883_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc883_vaiott_setup, - .init_hook = alc_hp_automute, - }, -}; - - /* * Pin config fixes */ @@ -11036,255 +3909,19 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { /* * BIOS auto configuration */ -static int alc882_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22); -} - -static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - hda_nid_t dac) -{ - int idx; - - /* set as output */ - alc_set_pin_output(codec, nid, pin_type); - - if (dac == 0x25) - idx = 4; - else if (dac >= 0x02 && dac <= 0x05) - idx = dac - 2; - else - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -static void alc882_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc882_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc882_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin, dac; - int i; - - if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) { - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_nid; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); - } - } - - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) { - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[0]; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); - } - } -} - -static void alc882_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } -} - -static void alc882_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int c; - - for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t conn_list[HDA_MAX_NUM_INPUTS]; - hda_nid_t nid = spec->capsrc_nids[c]; - unsigned int mux_idx; - const struct hda_input_mux *imux; - int conns, mute, idx, item; - - /* mute ADC */ - snd_hda_codec_write(codec, spec->adc_nids[c], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - - conns = snd_hda_get_connections(codec, nid, conn_list, - ARRAY_SIZE(conn_list)); - if (conns < 0) - continue; - mux_idx = c >= spec->num_mux_defs ? 0 : c; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - for (idx = 0; idx < conns; idx++) { - /* if the current connection is the selected one, - * unmute it as default - otherwise mute it - */ - mute = AMP_IN_MUTE(idx); - for (item = 0; item < imux->num_items; item++) { - if (imux->items[item].index == idx) { - if (spec->cur_mux[c] == item) - mute = AMP_IN_UNMUTE(idx); - break; - } - } - /* check if we have a selector or mixer - * we could check for the widget type instead, but - * just check for Amp-In presence (in case of mixer - * without amp-in there is something wrong, this - * function shouldn't be used or capsrc nid is wrong) - */ - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - mute); - else if (mute != AMP_IN_MUTE(idx)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - idx); - } - } -} - -/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - int type_idx = 0; - hda_nid_t nid; - const char *prev_label = NULL; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; - char boost_label[32]; - - 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(boost_label, sizeof(boost_label), - "%s Boost Volume", label); - err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - /* almost identical with ALC880 parser... */ static int alc882_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc882_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], - "Headphone"); - if (err < 0) - return err; - err = alc880_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); - if (err < 0) - return err; - err = alc882_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc883_auto_init_verbs); - /* if ADC 0x07 is available, initialize it, too */ - if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN) - add_verb(spec, alc882_adc1_init_verbs); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - return 1; /* config found */ + static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); } -/* additional initialization for auto-configuration model */ -static void alc882_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc882_auto_init_multi_out(codec); - alc882_auto_init_hp_out(codec); - alc882_auto_init_analog_input(codec); - alc882_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc882_quirks.c" +#endif static int patch_alc882(struct hda_codec *codec) { @@ -11297,6 +3934,8 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + switch (codec->vendor_id) { case 0x10ec0882: case 0x10ec0885: @@ -11307,106 +3946,71 @@ static int patch_alc882(struct hda_codec *codec) break; } - board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, - alc882_models, - alc882_cfg_tbl); + board_config = alc_board_config(codec, ALC882_MODEL_LAST, + alc882_models, alc882_cfg_tbl); - if (board_config < 0 || board_config >= ALC882_MODEL_LAST) - board_config = snd_hda_check_board_codec_sid_config(codec, + 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 || board_config >= ALC882_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC882_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC882_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_auto_parse_customize_define(codec); - if (board_config == ALC882_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc882_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC882_3ST_DIG; } +#endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - - if (board_config != ALC882_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc882_presets[board_config]); - spec->stream_analog_playback = &alc882_pcm_analog_playback; - spec->stream_analog_capture = &alc882_pcm_analog_capture; - /* FIXME: setup DAC5 */ - /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/ - spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; - - spec->stream_digital_playback = &alc882_pcm_digital_playback; - spec->stream_digital_capture = &alc882_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - int i, j; - spec->num_adc_nids = 0; - for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) { - const struct hda_input_mux *imux = spec->input_mux; - hda_nid_t cap; - hda_nid_t items[16]; - hda_nid_t nid = alc882_adc_nids[i]; - unsigned int wcap = get_wcaps(codec, nid); - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) - continue; - spec->private_adc_nids[spec->num_adc_nids] = nid; - err = snd_hda_get_connections(codec, nid, &cap, 1); - if (err < 0) - continue; - err = snd_hda_get_connections(codec, cap, items, - ARRAY_SIZE(items)); - if (err < 0) - continue; - for (j = 0; j < imux->num_items; j++) - if (imux->items[j].index >= err) - break; - if (j < imux->num_items) - continue; - spec->private_capsrc_nids[spec->num_adc_nids] = cap; - spec->num_adc_nids++; - } - spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; + 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); } - set_capture_mixer(codec); + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); - if (has_cdefine_beep(codec)) + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; - if (board_config == ALC882_AUTO) - spec->init_hook = alc882_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; alc_init_jacks(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -11421,1192 +4025,13 @@ static int patch_alc882(struct hda_codec *codec) /* * ALC262 support */ - -#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID -#define ALC262_DIGIN_NID ALC880_DIGIN_NID - -#define alc262_dac_nids alc260_dac_nids -#define alc262_adc_nids alc882_adc_nids -#define alc262_adc_nids_alt alc882_adc_nids_alt -#define alc262_capsrc_nids alc882_capsrc_nids -#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt - -#define alc262_modes alc260_modes -#define alc262_capture_source alc882_capture_source - -static const hda_nid_t alc262_dmic_adc_nids[1] = { - /* ADC0 */ - 0x09 -}; - -static const hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 }; - -static const struct snd_kcontrol_new alc262_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front 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("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("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* update HP, line and mono-out pins according to the master switch */ -#define alc262_hp_master_update alc260_hp_master_update - -static void alc262_hp_bpc_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static void alc262_hp_wildwest_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -#define alc262_hp_master_sw_get alc260_hp_master_sw_get -#define alc262_hp_master_sw_put alc260_hp_master_sw_put - -#define ALC262_HP_MASTER_SWITCH \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Master Playback Switch", \ - .info = snd_ctl_boolean_mono_info, \ - .get = alc262_hp_master_sw_get, \ - .put = alc262_hp_master_sw_put, \ - }, \ - { \ - .iface = NID_MAPPING, \ - .name = "Master Playback Switch", \ - .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \ - } - - -static const struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { - ALC262_HP_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { - ALC262_HP_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x1a, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { - HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Rear Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hp_t5735_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_hp_t5735_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -static const struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_hp_rp5700_verbs[] = { - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, - {} -}; - -static const struct hda_input_mux alc262_hp_rp5700_capture_source = { - .num_items = 1, - .items = { - { "Line", 0x1 }, - }, -}; - -/* bind hp and internal speaker mute (with plug check) as master switch */ -#define alc262_hippo_master_update alc262_hp_master_update -#define alc262_hippo_master_sw_get alc262_hp_master_sw_get -#define alc262_hippo_master_sw_put alc262_hp_master_sw_put - -#define ALC262_HIPPO_MASTER_SWITCH \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Master Playback Switch", \ - .info = snd_ctl_boolean_mono_info, \ - .get = alc262_hippo_master_sw_get, \ - .put = alc262_hippo_master_sw_put, \ - }, \ - { \ - .iface = NID_MAPPING, \ - .name = "Master Playback Switch", \ - .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \ - (SUBDEV_SPEAKER(0) << 16), \ - } - -static const struct snd_kcontrol_new alc262_hippo_mixer[] = { - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_hippo1_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hippo_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc262_hippo1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - - -static const struct snd_kcontrol_new alc262_sony_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_benq_t31_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc262_tyan_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, 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("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_tyan_verbs[] = { - /* Headphone automute */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* P11 AUX_IN, white 4-pin connector */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93}, - {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19}, - - {} -}; - -/* unsolicited event for HP jack sensing */ -static void alc262_tyan_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - - -#define alc262_capture_mixer alc882_capture_mixer -#define alc262_capture_alt_mixer alc882_capture_alt_mixer - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc262_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)}, - - /* Mute 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)}, - - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* 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}, - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {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)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, 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, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - - { } -}; - -static const struct hda_verb alc262_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc262_hippo1_unsol_verbs[] = { - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, - - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_sony_unsol_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_toshiba_s06_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x09}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static void alc262_toshiba_s06_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -/* - * nec model - * 0x15 = headphone - * 0x16 = internal speaker - * 0x18 = external mic - */ - -static const struct snd_kcontrol_new alc262_nec_mixer[] = { - HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_nec_verbs[] = { - /* Unmute Speaker */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Headphone */ - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* External mic to headphone */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* External mic to speaker */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {} -}; - -/* - * fujitsu model - * 0x14 = headphone/spdif-out, 0x15 = internal speaker, - * 0x1b = port replicator headphone out - */ - -#define ALC_HP_EVENT ALC880_HP_EVENT - -static const struct hda_verb alc262_fujitsu_unsol_verbs[] = { - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - -static const struct hda_verb alc262_lenovo_3000_init_verbs[] = { - /* Front Mic pin: input vref at 50% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {} -}; - -static const struct hda_input_mux alc262_fujitsu_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc262_HP_capture_source = { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - { "AUX IN", 0x6 }, - }, -}; - -static const struct hda_input_mux alc262_HP_D7000_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x2 }, - { "Line", 0x1 }, - { "CD", 0x4 }, - }, -}; - -static void alc262_fujitsu_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.hp_pins[1] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -/* bind volumes of both NID 0x0c and 0x0d */ -static const struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc262_fujitsu_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, - { - .iface = NID_MAPPING, - .name = "Master Playback Switch", - .private_value = 0x1b, - }, - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 void alc262_lenovo_3000_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static const struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 alc262_toshiba_rx1_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -/* additional init verbs for Benq laptops */ -static const struct hda_verb alc262_EAPD_verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3070}, - {} -}; - -static const struct hda_verb alc262_benq_t31_EAPD_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - - {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, - {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, - {} -}; - -/* Samsung Q1 Ultra Vista model setup */ -static const struct snd_kcontrol_new alc262_ultra_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, 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("Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Mic Boost Volume", 0x15, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb alc262_ultra_verbs[] = { - /* output mixer */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* speaker */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - /* internal mic */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* ADC, choose mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, - {} -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_ultra_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int mute; - - mute = 0; - /* auto-mute only when HP is used as HP */ - if (!spec->cur_mux[0]) { - spec->jack_present = snd_hda_jack_detect(codec, 0x15); - if (spec->jack_present) - mute = HDA_AMP_MUTE; - } - /* mute/unmute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - /* mute/unmute HP */ - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); -} - -/* unsolicited event for HP jack sensing */ -static void alc262_ultra_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_ultra_automute(codec); -} - -static const struct hda_input_mux alc262_ultra_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Headphone", 0x7 }, - }, -}; - -static int alc262_ultra_mux_enum_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 ret; - - ret = alc_mux_enum_put(kcontrol, ucontrol); - if (!ret) - return 0; - /* reprogram the HP pin as mic or HP according to the input source */ - snd_hda_codec_write_cache(codec, 0x15, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); - alc262_ultra_automute(codec); /* mute/unmute HP */ - return ret; -} - -static const struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc262_ultra_mux_enum_put, - }, - { - .iface = NID_MAPPING, - .name = "Capture Source", - .private_value = 0x15, - }, - { } /* end */ -}; - -/* We use two mixers depending on the output pin; 0x16 is a mono output - * and thus it's bound with a different mixer. - * This function returns which mixer amp should be used. - */ -static int alc262_check_volbit(hda_nid_t nid) -{ - if (!nid) - return 0; - else if (nid == 0x16) - return 2; - else - return 1; -} - -static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int *vbits, int idx) -{ - unsigned long val; - int vbit; - - vbit = alc262_check_volbit(nid); - if (!vbit) - return 0; - if (*vbits & vbit) /* a volume control for this mixer already there */ - return 0; - *vbits |= vbit; - if (vbit == 2) - val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val); -} - -static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, - const char *pfx, int idx) -{ - unsigned long val; - - if (!nid) - return 0; - if (nid == 0x16) - val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val); -} - -/* add playback controls from the parsed DAC table */ -static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) +static int alc262_parse_auto_config(struct hda_codec *codec) { - const char *pfx; - int vbits; - int i, err; - - spec->multiout.num_dacs = 1; /* only use one dac */ - spec->multiout.dac_nids = spec->private_dac_nids; - spec->private_dac_nids[0] = 2; - - pfx = alc_get_line_out_pfx(spec, true); - if (!pfx) - pfx = "Front"; - for (i = 0; i < 2; i++) { - err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i], - "Speaker", i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i], - "Headphone", i); - if (err < 0) - return err; - } - } - - vbits = alc262_check_volbit(cfg->line_out_pins[0]) | - alc262_check_volbit(cfg->speaker_pins[0]) | - alc262_check_volbit(cfg->hp_pins[0]); - if (vbits == 1 || vbits == 2) - pfx = "Master"; /* only one mixer is used */ - vbits = 0; - for (i = 0; i < 2; i++) { - err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx, - &vbits, i); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i], - "Speaker", &vbits, i); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i], - "Headphone", &vbits, i); - if (err < 0) - return err; - } - } - return 0; + static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); } -#define alc262_auto_create_input_ctls \ - alc882_auto_create_input_ctls - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc262_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)}, - - /* Mute 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)}, - - /* - * 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}, - - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {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)}, - - /* 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, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - - { } -}; - -static const struct hda_verb alc262_HP_BPC_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)}, - - /* Mute 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(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* 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}, - - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {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)}, - - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */ - /* Input mixer1: only unmute Mic */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, - - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } -}; - -static const struct hda_verb alc262_HP_BPC_WildWest_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)}, - - /* Mute 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(5)}, - {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 - 0x0e) - */ - /* 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}, - - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {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)}, - - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */ - - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - - /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - - /* 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, (0x7000 | (0x00 << 8))}, /*rear MIC*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/ - /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - - { } -}; - -static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, - - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - /* * Pin config fixes */ @@ -12645,396 +4070,11 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { #define alc262_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc262_pcm_analog_playback alc880_pcm_analog_playback -#define alc262_pcm_analog_capture alc880_pcm_analog_capture -#define alc262_pcm_digital_playback alc880_pcm_digital_playback -#define alc262_pcm_digital_capture alc880_pcm_digital_capture - -/* - * BIOS auto configuration - */ -static int alc262_parse_auto_config(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc262_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) { - if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc262_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc262_volume_init_verbs); - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - return 1; -} - -#define alc262_auto_init_multi_out alc882_auto_init_multi_out -#define alc262_auto_init_hp_out alc882_auto_init_hp_out -#define alc262_auto_init_analog_input alc882_auto_init_analog_input -#define alc262_auto_init_input_src alc882_auto_init_input_src - - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc262_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc262_auto_init_multi_out(codec); - alc262_auto_init_hp_out(codec); - alc262_auto_init_analog_input(codec); - alc262_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - /* - * configuration and preset */ -static const char * const alc262_models[ALC262_MODEL_LAST] = { - [ALC262_BASIC] = "basic", - [ALC262_HIPPO] = "hippo", - [ALC262_HIPPO_1] = "hippo_1", - [ALC262_FUJITSU] = "fujitsu", - [ALC262_HP_BPC] = "hp-bpc", - [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", - [ALC262_HP_TC_T5735] = "hp-tc-t5735", - [ALC262_HP_RP5700] = "hp-rp5700", - [ALC262_BENQ_ED8] = "benq", - [ALC262_BENQ_T31] = "benq-t31", - [ALC262_SONY_ASSAMD] = "sony-assamd", - [ALC262_TOSHIBA_S06] = "toshiba-s06", - [ALC262_TOSHIBA_RX1] = "toshiba-rx1", - [ALC262_ULTRA] = "ultra", - [ALC262_LENOVO_3000] = "lenovo-3000", - [ALC262_NEC] = "nec", - [ALC262_TYAN] = "tyan", - [ALC262_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc262_cfg_tbl[] = { - SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), - SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series", - ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", - ALC262_AUTO), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", - ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), - SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", - ALC262_HP_TC_T5735), - SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), - SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), - SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */ - SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), - SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), -#if 0 /* disable the quirk since model=auto works better in recent versions */ - SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", - ALC262_SONY_ASSAMD), +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc262_quirks.c" #endif - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", - ALC262_TOSHIBA_RX1), - SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1", - ALC262_ULTRA), - SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), - SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), - {} -}; - -static const struct alc_config_preset alc262_presets[] = { - [ALC262_BASIC] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_HIPPO] = { - .mixers = { alc262_hippo_mixer }, - .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_HIPPO_1] = { - .mixers = { alc262_hippo1_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo1_setup, - .init_hook = alc_inithook, - }, - [ALC262_FUJITSU] = { - .mixers = { alc262_fujitsu_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, - alc262_fujitsu_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_fujitsu_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_fujitsu_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC] = { - .mixers = { alc262_HP_BPC_mixer }, - .init_verbs = { alc262_HP_BPC_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_bpc_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC_D7000_WF] = { - .mixers = { alc262_HP_BPC_WildWest_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_D7000_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_wildwest_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_BPC_D7000_WL] = { - .mixers = { alc262_HP_BPC_WildWest_mixer, - alc262_HP_BPC_WildWest_option_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_D7000_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_wildwest_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_TC_T5735] = { - .mixers = { alc262_hp_t5735_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hp_t5735_setup, - .init_hook = alc_inithook, - }, - [ALC262_HP_RP5700] = { - .mixers = { alc262_hp_rp5700_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_hp_rp5700_capture_source, - }, - [ALC262_BENQ_ED8] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_SONY_ASSAMD] = { - .mixers = { alc262_sony_mixer }, - .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_BENQ_T31] = { - .mixers = { alc262_benq_t31_mixer }, - .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, - alc_hp15_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer }, - .cap_mixer = alc262_ultra_capture_mixer, - .init_verbs = { alc262_ultra_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_ultra_capture_source, - .adc_nids = alc262_adc_nids, /* ADC0 */ - .capsrc_nids = alc262_capsrc_nids, - .num_adc_nids = 1, /* single ADC */ - .unsol_event = alc262_ultra_unsol_event, - .init_hook = alc262_ultra_automute, - }, - [ALC262_LENOVO_3000] = { - .mixers = { alc262_lenovo_3000_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, - alc262_lenovo_3000_unsol_verbs, - alc262_lenovo_3000_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_fujitsu_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_lenovo_3000_setup, - .init_hook = alc_inithook, - }, - [ALC262_NEC] = { - .mixers = { alc262_nec_mixer }, - .init_verbs = { alc262_nec_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_TOSHIBA_S06] = { - .mixers = { alc262_toshiba_s06_mixer }, - .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, - alc262_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .capsrc_nids = alc262_dmic_capsrc_nids, - .dac_nids = alc262_dac_nids, - .adc_nids = alc262_dmic_adc_nids, /* ADC0 */ - .num_adc_nids = 1, /* single ADC */ - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_toshiba_s06_setup, - .init_hook = alc_inithook, - }, - [ALC262_TOSHIBA_RX1] = { - .mixers = { alc262_toshiba_rx1_mixer }, - .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_hippo_setup, - .init_hook = alc_inithook, - }, - [ALC262_TYAN] = { - .mixers = { alc262_tyan_mixer }, - .init_verbs = { alc262_init_verbs, alc262_tyan_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc262_tyan_setup, - .init_hook = alc_hp_automute, - }, -}; static int patch_alc262(struct hda_codec *codec) { @@ -13047,6 +4087,9 @@ static int patch_alc262(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + + spec->mixer_nid = 0x0b; + #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is * under-run @@ -13063,96 +4106,65 @@ static int patch_alc262(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x0a, 10); - board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, - alc262_models, - alc262_cfg_tbl); + board_config = alc_board_config(codec, ALC262_MODEL_LAST, + alc262_models, alc262_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC262_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC262_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC262_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc262_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC262_BASIC; } +#endif } + if (board_config != ALC_MODEL_AUTO) + setup_preset(codec, &alc262_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); + } + + 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) { alc_free(codec); return err; } - } - - if (board_config != ALC262_AUTO) - setup_preset(codec, &alc262_presets[board_config]); - - spec->stream_analog_playback = &alc262_pcm_analog_playback; - spec->stream_analog_capture = &alc262_pcm_analog_capture; - - spec->stream_digital_playback = &alc262_pcm_digital_playback; - spec->stream_digital_capture = &alc262_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - int i; - /* check whether the digital-mic has to be supported */ - for (i = 0; i < spec->input_mux->num_items; i++) { - if (spec->input_mux->items[i].index >= 9) - break; - } - if (i < spec->input_mux->num_items) { - /* use only ADC0 */ - spec->adc_nids = alc262_dmic_adc_nids; - spec->num_adc_nids = 1; - spec->capsrc_nids = alc262_dmic_capsrc_nids; - } else { - /* all analog inputs */ - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - /* get type */ - wcap = get_wcaps_type(wcap); - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc262_adc_nids_alt; - spec->num_adc_nids = - ARRAY_SIZE(alc262_adc_nids_alt); - spec->capsrc_nids = alc262_capsrc_nids_alt; - } else { - spec->adc_nids = alc262_adc_nids; - spec->num_adc_nids = - ARRAY_SIZE(alc262_adc_nids); - spec->capsrc_nids = alc262_capsrc_nids; - } - } - } - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; - if (board_config == ALC262_AUTO) - spec->init_hook = alc262_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -13165,51 +4177,8 @@ static int patch_alc262(struct hda_codec *codec) } /* - * ALC268 channel source setting (2 channel) + * ALC268 */ -#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID -#define alc268_modes alc260_modes - -static const hda_nid_t alc268_dac_nids[2] = { - /* front, hp */ - 0x02, 0x03 -}; - -static const hda_nid_t alc268_adc_nids[2] = { - /* ADC0-1 */ - 0x08, 0x07 -}; - -static const hda_nid_t alc268_adc_nids_alt[1] = { - /* ADC0 */ - 0x08 -}; - -static const hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; - -static const struct snd_kcontrol_new alc268_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_toshiba_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - /* bind Beep switches of both NID 0x0f and 0x10 */ static const struct hda_bind_ctls alc268_bind_beep_sw = { .ops = &snd_hda_bind_sw, @@ -13226,846 +4195,36 @@ static const struct snd_kcontrol_new alc268_beep_mixer[] = { { } }; -static const struct hda_verb alc268_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -/* Toshiba specific */ -static const struct hda_verb alc268_toshiba_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } /* end */ -}; - -/* Acer specific */ -/* bind volumes of both NID 0x02 and 0x03 */ -static const struct hda_bind_ctls alc268_acer_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static void alc268_acer_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -#define alc268_acer_master_sw_get alc262_hp_master_sw_get -#define alc268_acer_master_sw_put alc262_hp_master_sw_put - -static const struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x15, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_acer_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, - .info = snd_ctl_boolean_mono_info, - .get = alc268_acer_master_sw_get, - .put = alc268_acer_master_sw_put, - }, - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc268_acer_aspire_one_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x06}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017}, - { } -}; - -static const struct hda_verb alc268_acer_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - { } -}; - -/* unsolicited event for HP jack sensing */ -#define alc268_toshiba_setup alc262_hippo_setup - -static void alc268_acer_lc_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 6; - spec->auto_mic = 1; -} - -static const struct snd_kcontrol_new alc268_dell_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc268_dell_verbs[] = { - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } -}; - -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc268_dell_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -static const struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_verb alc267_quanta_il1_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - { } -}; - -static void alc267_quanta_il1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; -} - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc268_base_init_verbs[] = { - /* Unmute DAC0-1 and set vol = 0 */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Set up output mixers (0x0c - 0x0e) - */ - /* set vol=0 to output mixers */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - /* set PCBEEP vol = 0, mute connections */ +/* set PCBEEP vol = 0, mute connections */ +static const struct hda_verb alc268_beep_init_verbs[] = { {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* Unmute Selector 23h,24h and set the default input to mic-in */ - - {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - { } }; /* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc268_volume_init_verbs[] = { - /* set output DAC */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - /* set PCBEEP vol = 0, mute connections */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - { } -}; - -static const struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc268_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - _DEFINE_CAPSRC(1), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc268_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT), - _DEFINE_CAPSRC(2), - { } /* end */ -}; - -static const struct hda_input_mux alc268_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x3 }, - }, -}; - -static const struct hda_input_mux alc268_acer_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc268_acer_dmic_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x6 }, - { "Line", 0x2 }, - }, -}; - -#ifdef CONFIG_SND_DEBUG -static const struct snd_kcontrol_new alc268_test_mixer[] = { - /* Volume widgets */ - HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), - HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), - HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), - HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), - HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), - HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), - /* The below appears problematic on some hardwares */ - /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ - HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), - - /* Modes for retasking pin widgets */ - ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), - ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), - - /* 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 SPDIF output pin to be enabled. - * The ALC268 does not have an SPDIF input. - */ - ALC_SPDIF_CTRL_SWITCH("SPDIF Playback 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 */ -}; -#endif - -/* create input playback/capture controls for the given pin */ -static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, - const char *ctlname, int idx) -{ - hda_nid_t dac; - int err; - - switch (nid) { - case 0x14: - case 0x16: - dac = 0x02; - break; - case 0x15: - case 0x1a: /* ALC259/269 only */ - case 0x1b: /* ALC259/269 only */ - case 0x21: /* ALC269vb has this pin, too */ - dac = 0x03; - break; - default: - snd_printd(KERN_WARNING "hda_codec: " - "ignoring pin 0x%x as unknown\n", nid); - return 0; - } - if (spec->multiout.dac_nids[0] != dac && - spec->multiout.dac_nids[1] != dac) { - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, - HDA_COMPOSE_AMP_VAL(dac, 3, idx, - HDA_OUTPUT)); - if (err < 0) - return err; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - - if (nid != 0x16) - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); - else /* mono */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int err; - - spec->multiout.dac_nids = spec->private_dac_nids; - - nid = cfg->line_out_pins[0]; - if (nid) { - const char *name; - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - name = "Speaker"; - else - name = "Front"; - err = alc268_new_analog_output(spec, nid, name, 0); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid == 0x1d) { - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } else if (nid) { - err = alc268_new_analog_output(spec, nid, "Speaker", 0); - if (err < 0) - return err; - } - nid = cfg->hp_pins[0]; - if (nid) { - err = alc268_new_analog_output(spec, nid, "Headphone", 0); - if (err < 0) - return err; - } - - nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; - if (nid == 0x16) { - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* create playback/capture controls for input pins */ -static int alc268_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24); -} - -static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type) -{ - int idx; - - alc_set_pin_output(codec, nid, pin_type); - if (nid == 0x14 || nid == 0x16) - idx = 0; - else - idx = 1; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -static void alc268_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc268_auto_set_output_and_unmute(codec, nid, pin_type); - } -} - -static void alc268_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - pin = spec->autocfg.speaker_pins[i]; - alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); - } - if (spec->autocfg.mono_out_pin) - snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); -} - -static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; - hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; - hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; - unsigned int dac_vol1, dac_vol2; - - if (line_nid == 0x1d || speaker_nid == 0x1d) { - snd_hda_codec_write(codec, speaker_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* mute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } else { - /* unmute mixer inputs from 0x1d */ - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); - } - - dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ - if (line_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (line_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (hp_nid == 0x14) - dac_vol2 = AMP_OUT_ZERO; - else if (hp_nid == 0x15) - dac_vol1 = AMP_OUT_ZERO; - if (line_nid != 0x16 || hp_nid != 0x16 || - spec->autocfg.line_out_pins[1] != 0x16 || - spec->autocfg.line_out_pins[2] != 0x16) - dac_vol1 = dac_vol2 = AMP_OUT_ZERO; - - snd_hda_codec_write(codec, 0x02, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); - snd_hda_codec_write(codec, 0x03, 0, - AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); -} - -/* pcm configuration: identical with ALC880 */ -#define alc268_pcm_analog_playback alc880_pcm_analog_playback -#define alc268_pcm_analog_capture alc880_pcm_analog_capture -#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture -#define alc268_pcm_digital_playback alc880_pcm_digital_playback - -/* * BIOS auto configuration */ static int alc268_parse_auto_config(struct hda_codec *codec) { + static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc268_ignore[] = { 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc268_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) { - if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; + int err = alc_parse_auto_config(codec, NULL, alc268_ssids); + if (err > 0) { + if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { + add_mixer(spec, alc268_beep_mixer); + add_verb(spec, alc268_beep_init_verbs); } - return 0; /* can't find valid BIOS pin config */ } - err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc268_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - dig_only: - /* digital only support output */ - alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) - add_mixer(spec, alc268_beep_mixer); - - add_verb(spec, alc268_volume_init_verbs); - spec->num_mux_defs = 2; - spec->input_mux = &spec->private_imux[0]; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - return 1; -} - -#define alc268_auto_init_analog_input alc882_auto_init_analog_input -#define alc268_auto_init_input_src alc882_auto_init_input_src - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc268_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc268_auto_init_multi_out(codec); - alc268_auto_init_hp_out(codec); - alc268_auto_init_mono_speaker_out(codec); - alc268_auto_init_analog_input(codec); - alc268_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + return err; } /* - * configuration and preset */ -static const char * const alc268_models[ALC268_MODEL_LAST] = { - [ALC267_QUANTA_IL1] = "quanta-il1", - [ALC268_3ST] = "3stack", - [ALC268_TOSHIBA] = "toshiba", - [ALC268_ACER] = "acer", - [ALC268_ACER_DMIC] = "acer-dmic", - [ALC268_ACER_ASPIRE_ONE] = "acer-aspire", - [ALC268_DELL] = "dell", - [ALC268_ZEPTO] = "zepto", -#ifdef CONFIG_SND_DEBUG - [ALC268_TEST] = "test", -#endif - [ALC268_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc268_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One", - ALC268_ACER_ASPIRE_ONE), - SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), - SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron 910", ALC268_AUTO), - SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0, - "Dell Inspiron Mini9/Vostro A90", ALC268_DELL), - /* almost compatible with toshiba but with optional digital outs; - * auto-probing seems working fine - */ - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", - ALC268_AUTO), - SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), - SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), - SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), - {} -}; - -/* Toshiba laptops have no unique PCI SSID but only codec SSID */ -static const struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { - SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO), - SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO), - SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05", - ALC268_TOSHIBA), - {} -}; - -static const struct alc_config_preset alc268_presets[] = { - [ALC267_QUANTA_IL1] = { - .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, - alc268_capture_nosrc_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc267_quanta_il1_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc267_quanta_il1_setup, - .init_hook = alc_inithook, - }, - [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, - .init_verbs = { alc268_base_init_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC268_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - }, - [ALC268_TOSHIBA] = { - .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_toshiba_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_toshiba_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_acer_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_acer_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER_DMIC] = { - .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_acer_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_acer_dmic_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_setup, - .init_hook = alc_inithook, - }, - [ALC268_ACER_ASPIRE_ONE] = { - .mixers = { alc268_acer_aspire_one_mixer, - alc268_beep_mixer, - alc268_capture_nosrc_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_acer_aspire_one_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_acer_lc_setup, - .init_hook = alc_inithook, - }, - [ALC268_DELL] = { - .mixers = { alc268_dell_mixer, alc268_beep_mixer, - alc268_capture_nosrc_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_dell_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_dell_setup, - .init_hook = alc_inithook, - }, - [ALC268_ZEPTO] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_toshiba_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC268_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc268_toshiba_setup, - .init_hook = alc_inithook, - }, -#ifdef CONFIG_SND_DEBUG - [ALC268_TEST] = { - .mixers = { alc268_test_mixer, alc268_capture_mixer }, - .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, - alc268_volume_init_verbs }, - .num_dacs = ARRAY_SIZE(alc268_dac_nids), - .dac_nids = alc268_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), - .adc_nids = alc268_adc_nids_alt, - .capsrc_nids = alc268_capsrc_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC268_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc268_modes), - .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - }, +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc268_quirks.c" #endif -}; static int patch_alc268(struct hda_codec *codec) { @@ -14079,43 +4238,41 @@ static int patch_alc268(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST, - alc268_models, - alc268_cfg_tbl); + /* ALC268 has no aa-loopback mixer */ + + board_config = alc_board_config(codec, ALC268_MODEL_LAST, + alc268_models, alc268_cfg_tbl); - if (board_config < 0 || board_config >= ALC268_MODEL_LAST) - board_config = snd_hda_check_board_codec_sid_config(codec, + if (board_config < 0) + board_config = alc_board_codec_sid_config(codec, ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); - if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC268_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC268_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC268_3ST; } +#endif } - if (board_config != ALC268_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc268_presets[board_config]); - spec->stream_analog_playback = &alc268_pcm_analog_playback; - spec->stream_analog_capture = &alc268_pcm_analog_capture; - spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture; - - spec->stream_digital_playback = &alc268_pcm_digital_playback; - has_beep = 0; for (i = 0; i < spec->num_mixers; i++) { if (spec->mixers[i] == alc268_beep_mixer) { @@ -14139,35 +4296,20 @@ static int patch_alc268(struct hda_codec *codec) (0 << AC_AMPCAP_MUTE_SHIFT)); } - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - spec->capsrc_nids = alc268_capsrc_nids; - /* get type */ - wcap = get_wcaps_type(wcap); - if (spec->auto_mic || - wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc268_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); - if (spec->auto_mic) - fixup_automic_adc(codec); - if (spec->auto_mic || spec->input_mux->num_items == 1) - add_mixer(spec, alc268_capture_nosrc_mixer); - else - add_mixer(spec, alc268_capture_alt_mixer); - } else { - spec->adc_nids = alc268_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); - add_mixer(spec, alc268_capture_mixer); - } + 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); + spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; - if (board_config == ALC268_AUTO) - spec->init_hook = alc268_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -14176,498 +4318,12 @@ static int patch_alc268(struct hda_codec *codec) } /* - * ALC269 channel source setting (2 channel) - */ -#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID - -#define alc269_dac_nids alc260_dac_nids - -static const hda_nid_t alc269_adc_nids[1] = { - /* ADC1 */ - 0x08, -}; - -static const hda_nid_t alc269_capsrc_nids[1] = { - 0x23, -}; - -static const hda_nid_t alc269vb_adc_nids[1] = { - /* ADC1 */ - 0x09, -}; - -static const hda_nid_t alc269vb_capsrc_nids[1] = { - 0x22, -}; - -static const hda_nid_t alc269_adc_candidates[] = { - 0x08, 0x09, 0x07, 0x11, -}; - -#define alc269_modes alc260_modes -#define alc269_capture_source alc880_lg_lw_capture_source - -static const struct snd_kcontrol_new alc269_base_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, 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, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_acer_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc269_lifebook_mixer[] = { - /* output mixer control */ - HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_acer_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x1b, 0, HDA_INPUT), - { } -}; - -static const struct snd_kcontrol_new alc269_laptop_mixer[] = { - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269vb_laptop_mixer[] = { - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269_asus_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* capture mixer elements */ -static const struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - { } /* end */ -}; - -/* FSC amilo */ -#define alc269_fujitsu_mixer alc269_laptop_mixer - -static const struct hda_verb alc269_quanta_fl1_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - { } -}; - -static const struct hda_verb alc269_lifebook_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) -{ - alc_hp_automute(codec); - - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x680); - - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x480); -} - -#define alc269_lifebook_speaker_automute \ - alc269_quanta_fl1_speaker_automute - -static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) -{ - unsigned int present_laptop; - unsigned int present_dock; - - present_laptop = snd_hda_jack_detect(codec, 0x18); - present_dock = snd_hda_jack_detect(codec, 0x1b); - - /* Laptop mic port overrides dock mic port, design decision */ - if (present_dock) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x3); - if (present_laptop) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x0); - if (!present_dock && !present_laptop) - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, 0x1); -} - -static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc269_quanta_fl1_speaker_automute(codec); - break; - case ALC880_MIC_EVENT: - alc_mic_automute(codec); - break; - } -} - -static void alc269_lifebook_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc269_lifebook_speaker_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc269_lifebook_mic_autoswitch(codec); -} - -static void alc269_quanta_fl1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) -{ - alc269_quanta_fl1_speaker_automute(codec); - alc_mic_automute(codec); -} - -static void alc269_lifebook_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.hp_pins[1] = 0x1a; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; -} - -static void alc269_lifebook_init_hook(struct hda_codec *codec) -{ - alc269_lifebook_speaker_automute(codec); - alc269_lifebook_mic_autoswitch(codec); -} - -static const struct hda_verb alc269_laptop_dmic_init_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc269_laptop_amic_init_verbs[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc269vb_laptop_dmic_init_verbs[] = { - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x06}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc269vb_laptop_amic_init_verbs[] = { - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc271_acer_dmic_verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x22, AC_VERB_SET_CONNECT_SEL, 6}, - { } -}; - -static void alc269_laptop_amic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -static void alc269_laptop_dmic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 5; - spec->auto_mic = 1; -} - -static void alc269vb_laptop_amic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 6; - spec->auto_mic = 1; -} - -/* - * generic initialization of ADC, input mixers and output mixers + * ALC269 */ -static const struct hda_verb alc269_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* - * Set up output mixers (0x02 - 0x03) - */ - /* set vol=0 to output mixers */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, 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_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)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {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}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* FIXME: use Mux-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* set EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc269vb_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* - * Set up output mixers (0x02 - 0x03) - */ - /* set vol=0 to output mixers */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, 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_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)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {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}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* FIXME: use Mux-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* set EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -#define alc269_auto_create_multi_out_ctls \ - alc268_auto_create_multi_out_ctls -#define alc269_auto_create_input_ctls \ - alc268_auto_create_input_ctls - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc269_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc269_pcm_analog_playback alc880_pcm_analog_playback -#define alc269_pcm_analog_capture alc880_pcm_analog_capture -#define alc269_pcm_digital_playback alc880_pcm_digital_playback -#define alc269_pcm_digital_capture alc880_pcm_digital_capture - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -14675,9 +4331,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ /* NID is set in alc_build_pcms */ .ops = { - .open = alc880_playback_pcm_open, - .prepare = alc880_playback_pcm_prepare, - .cleanup = alc880_playback_pcm_cleanup + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; @@ -14718,44 +4374,11 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) } #endif /* CONFIG_SND_HDA_POWER_SAVE */ -static int alc275_setup_dual_adc(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic) - return 0; - if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) || - (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) { - if (spec->ext_mic.pin <= 0x12) { - spec->private_adc_nids[0] = 0x08; - spec->private_adc_nids[1] = 0x11; - spec->private_capsrc_nids[0] = 0x23; - spec->private_capsrc_nids[1] = 0x22; - } else { - spec->private_adc_nids[0] = 0x11; - spec->private_adc_nids[1] = 0x08; - spec->private_capsrc_nids[0] = 0x22; - spec->private_capsrc_nids[1] = 0x23; - } - spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; - spec->num_adc_nids = 2; - spec->dual_adc_switch = 1; - snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n", - spec->adc_nids[0], spec->adc_nids[1]); - return 1; - } - return 0; -} - /* different alc269-variants */ enum { - ALC269_TYPE_NORMAL, - ALC269_TYPE_ALC258, - ALC269_TYPE_ALC259, + ALC269_TYPE_ALC269VA, ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC270, - ALC269_TYPE_ALC271X, + ALC269_TYPE_ALC269VC, }; /* @@ -14763,76 +4386,14 @@ enum { */ static int alc269_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc269_ignore); - if (err < 0) - return err; - - err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - if (spec->codec_variant == ALC269_TYPE_NORMAL) - err = alc269_auto_create_input_ctls(codec, &spec->autocfg); - else - err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0, - 0x22, 0); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - if (spec->codec_variant != ALC269_TYPE_NORMAL) { - add_verb(spec, alc269vb_init_verbs); - alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); - } else { - add_verb(spec, alc269_init_verbs); - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - } - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - if (!alc275_setup_dual_adc(codec)) - fillup_priv_adc_nids(codec, alc269_adc_candidates, - sizeof(alc269_adc_candidates)); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(codec); - - return 1; -} - -#define alc269_auto_init_multi_out alc268_auto_init_multi_out -#define alc269_auto_init_hp_out alc268_auto_init_hp_out -#define alc269_auto_init_analog_input alc882_auto_init_analog_input -#define alc269_auto_init_input_src alc882_auto_init_input_src - - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc269_auto_init(struct hda_codec *codec) -{ + static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; - alc269_auto_init_multi_out(codec); - alc269_auto_init_hp_out(codec); - alc269_auto_init_analog_input(codec); - if (!spec->dual_adc_switch) - alc269_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + const hda_nid_t *ssids = spec->codec_variant == ALC269_TYPE_ALC269VA ? + alc269va_ssids : alc269_ssids; + + return alc_parse_auto_config(codec, alc269_ignore, ssids); } static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) @@ -14855,7 +4416,7 @@ static void alc269_shutup(struct hda_codec *codec) } } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int alc269_resume(struct hda_codec *codec) { if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { @@ -14878,7 +4439,7 @@ static int alc269_resume(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); return 0; } -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ static void alc269_fixup_hweq(struct hda_codec *codec, const struct alc_fixup *fix, int action) @@ -14908,6 +4469,21 @@ static void alc271_fixup_dmic(struct hda_codec *codec, snd_hda_sequence_write(codec, verbs); } +static void alc269_fixup_pcm_44k(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action != ALC_FIXUP_ACT_PROBE) + return; + + /* Due to a hardware problem on Lenovo Ideadpad, we need to + * fix the sample rate of analog I/O to 44.1kHz + */ + spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -14917,6 +4493,7 @@ enum { ALC269_FIXUP_LENOVO_EAPD, ALC275_FIXUP_SONY_HWEQ, ALC271_FIXUP_DMIC, + ALC269_FIXUP_PCM_44K, }; static const struct alc_fixup alc269_fixups[] = { @@ -14975,9 +4552,14 @@ static const struct alc_fixup alc269_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc271_fixup_dmic, }, + [ALC269_FIXUP_PCM_44K] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_pcm_44k, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), @@ -14989,209 +4571,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), {} }; -/* - * configuration and preset - */ -static const char * const alc269_models[ALC269_MODEL_LAST] = { - [ALC269_BASIC] = "basic", - [ALC269_QUANTA_FL1] = "quanta", - [ALC269_AMIC] = "laptop-amic", - [ALC269_DMIC] = "laptop-dmic", - [ALC269_FUJITSU] = "fujitsu", - [ALC269_LIFEBOOK] = "lifebook", - [ALC269_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc269_cfg_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), - SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER), - SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269VB_AMIC), - SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), - SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), - SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), - SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), - SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC), - SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC), - SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC), - {} -}; - -static const struct alc_config_preset alc269_presets[] = { - [ALC269_BASIC] = { - .mixers = { alc269_base_mixer }, - .init_verbs = { alc269_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - }, - [ALC269_QUANTA_FL1] = { - .mixers = { alc269_quanta_fl1_mixer }, - .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .unsol_event = alc269_quanta_fl1_unsol_event, - .setup = alc269_quanta_fl1_setup, - .init_hook = alc269_quanta_fl1_init_hook, - }, - [ALC269_AMIC] = { - .mixers = { alc269_laptop_mixer }, - .cap_mixer = alc269_laptop_analog_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_amic_setup, - .init_hook = alc_inithook, - }, - [ALC269_DMIC] = { - .mixers = { alc269_laptop_mixer }, - .cap_mixer = alc269_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269VB_AMIC] = { - .mixers = { alc269vb_laptop_mixer }, - .cap_mixer = alc269vb_laptop_analog_capture_mixer, - .init_verbs = { alc269vb_init_verbs, - alc269vb_laptop_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_amic_setup, - .init_hook = alc_inithook, - }, - [ALC269VB_DMIC] = { - .mixers = { alc269vb_laptop_mixer }, - .cap_mixer = alc269vb_laptop_digital_capture_mixer, - .init_verbs = { alc269vb_init_verbs, - alc269vb_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269_FUJITSU] = { - .mixers = { alc269_fujitsu_mixer }, - .cap_mixer = alc269_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, - alc269_laptop_dmic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc269_laptop_dmic_setup, - .init_hook = alc_inithook, - }, - [ALC269_LIFEBOOK] = { - .mixers = { alc269_lifebook_mixer }, - .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .unsol_event = alc269_lifebook_unsol_event, - .setup = alc269_lifebook_setup, - .init_hook = alc269_lifebook_init_hook, - }, - [ALC271_ACER] = { - .mixers = { alc269_asus_mixer }, - .cap_mixer = alc269vb_laptop_digital_capture_mixer, - .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs }, - .num_dacs = ARRAY_SIZE(alc269_dac_nids), - .dac_nids = alc269_dac_nids, - .adc_nids = alc262_dmic_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids), - .capsrc_nids = alc262_dmic_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc269_modes), - .channel_mode = alc269_modes, - .input_mux = &alc269_capture_source, - .dig_out_nid = ALC880_DIGOUT_NID, - .unsol_event = alc_sku_unsol_event, - .setup = alc269vb_laptop_dmic_setup, - .init_hook = alc_inithook, - }, -}; - static int alc269_fill_coef(struct hda_codec *codec) { int val; @@ -15234,6 +4619,12 @@ static int alc269_fill_coef(struct hda_codec *codec) return 0; } +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc269_quirks.c" +#endif + static int patch_alc269(struct hda_codec *codec) { struct alc_spec *spec; @@ -15246,116 +4637,105 @@ static int patch_alc269(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + alc_auto_parse_customize_define(codec); if (codec->vendor_id == 0x10ec0269) { + spec->codec_variant = ALC269_TYPE_ALC269VA; coef = alc_read_coef_idx(codec, 0); if ((coef & 0x00f0) == 0x0010) { if (codec->bus->pci->subsystem_vendor == 0x1025 && spec->cdefine.platform_type == 1) { alc_codec_rename(codec, "ALC271X"); - spec->codec_variant = ALC269_TYPE_ALC271X; - } else if ((coef & 0xf000) == 0x1000) { - spec->codec_variant = ALC269_TYPE_ALC270; } else if ((coef & 0xf000) == 0x2000) { alc_codec_rename(codec, "ALC259"); - spec->codec_variant = ALC269_TYPE_ALC259; } else if ((coef & 0xf000) == 0x3000) { alc_codec_rename(codec, "ALC258"); - spec->codec_variant = ALC269_TYPE_ALC258; + } else if ((coef & 0xfff0) == 0x3010) { + alc_codec_rename(codec, "ALC277"); } else { alc_codec_rename(codec, "ALC269VB"); - spec->codec_variant = ALC269_TYPE_ALC269VB; } + spec->codec_variant = ALC269_TYPE_ALC269VB; + } else if ((coef & 0x00f0) == 0x0020) { + if (coef == 0xa023) + alc_codec_rename(codec, "ALC259"); + else if (coef == 0x6023) + alc_codec_rename(codec, "ALC281X"); + else if (codec->bus->pci->subsystem_vendor == 0x17aa && + codec->bus->pci->subsystem_device == 0x21f3) + alc_codec_rename(codec, "ALC3202"); + else + alc_codec_rename(codec, "ALC269VC"); + spec->codec_variant = ALC269_TYPE_ALC269VC; } else alc_fix_pll_init(codec, 0x20, 0x04, 15); alc269_fill_coef(codec); } - board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, - alc269_models, - alc269_cfg_tbl); + board_config = alc_board_config(codec, ALC269_MODEL_LAST, + alc269_models, alc269_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC269_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC269_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, NULL, alc269_fixup_tbl, alc269_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC269_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC269_BASIC; } +#endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - - if (board_config != ALC269_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc269_presets[board_config]); - if (board_config == ALC269_QUANTA_FL1) { - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz - */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; - } else if (spec->dual_adc_switch) { - spec->stream_analog_playback = &alc269_pcm_analog_playback; - /* switch ADC dynamically */ - spec->stream_analog_capture = &dualmic_pcm_analog_capture; - } else { - spec->stream_analog_playback = &alc269_pcm_analog_playback; - spec->stream_analog_capture = &alc269_pcm_analog_capture; - } - spec->stream_digital_playback = &alc269_pcm_digital_playback; - spec->stream_digital_capture = &alc269_pcm_digital_capture; - - if (!spec->adc_nids) { /* wasn't filled automatically? use default */ - if (spec->codec_variant == ALC269_TYPE_NORMAL) { - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; - } else { - spec->adc_nids = alc269vb_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); - spec->capsrc_nids = alc269vb_capsrc_nids; - } + 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->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - if (has_cdefine_beep(codec)) + + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + } alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM codec->patch_ops.resume = alc269_resume; #endif - if (board_config == ALC269_AUTO) - spec->init_hook = alc269_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc269_shutup; alc_init_jacks(codec); @@ -15370,883 +4750,14 @@ static int patch_alc269(struct hda_codec *codec) } /* - * ALC861 channel source setting (2/6 channel selection for 3-stack) - */ - -/* - * set the path ways for 2 channel output - * need to set the codec line out and mic 1 pin widgets to inputs + * ALC861 */ -static const struct hda_verb alc861_threestack_ch2_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* set pin widget 18h (mic1/2) for input, for mic also enable - * the vref - */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ -#endif - { } /* end */ -}; -/* - * 6ch mode - * need to set the codec line out and mic 1 pin widgets to outputs - */ -static const struct hda_verb alc861_threestack_ch6_init[] = { - /* set pin widget 1Ah (line in) for output (Back Surround)*/ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* set pin widget 18h (mic1) for output (CLFE)*/ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - - { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ -#endif - { } /* end */ -}; - -static const struct hda_channel_mode alc861_threestack_modes[2] = { - { 2, alc861_threestack_ch2_init }, - { 6, alc861_threestack_ch6_init }, -}; -/* Set mic1 as input and unmute the mixer */ -static const struct hda_verb alc861_uniwill_m31_ch2_init[] = { - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { } /* end */ -}; -/* Set mic1 as output and mute mixer */ -static const struct hda_verb alc861_uniwill_m31_ch4_init[] = { - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { } /* end */ -}; - -static const struct hda_channel_mode alc861_uniwill_m31_modes[2] = { - { 2, alc861_uniwill_m31_ch2_init }, - { 4, alc861_uniwill_m31_ch4_init }, -}; - -/* Set mic1 and line-in as input and unmute the mixer */ -static const struct hda_verb alc861_asus_ch2_init[] = { - /* set pin widget 1Ah (line in) for input */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* set pin widget 18h (mic1/2) for input, for mic also enable - * the vref - */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ -#endif - { } /* end */ -}; -/* Set mic1 nad line-in as output and mute mixer */ -static const struct hda_verb alc861_asus_ch6_init[] = { - /* set pin widget 1Ah (line in) for output (Back Surround)*/ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ - /* set pin widget 18h (mic1) for output (CLFE)*/ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */ - { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, -#if 0 - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ -#endif - { } /* end */ -}; - -static const struct hda_channel_mode alc861_asus_modes[2] = { - { 2, alc861_asus_ch2_init }, - { 6, alc861_asus_ch6_init }, -}; - -/* patch-ALC861 */ - -static const struct snd_kcontrol_new alc861_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), - - /*Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_3ST_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ - - /* Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, - .private_value = ARRAY_SIZE(alc861_threestack_modes), - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_toshiba_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */ - - /* Input mixer control */ - /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, - .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes), - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861_asus_mixer[] = { - /* output mixer control */ - HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), - - /* Input mixer control */ - HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, 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, - .private_value = ARRAY_SIZE(alc861_asus_modes), - }, - { } -}; - -/* additional mixer */ -static const struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), - { } -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc861_base_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute DAC0~3 & spdif out*/ - {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}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - - { } -}; - -static const struct hda_verb alc861_threestack_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {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}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -static const struct hda_verb alc861_uniwill_m31_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - /* this has to be set to VREF80 */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {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}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -static const struct hda_verb alc861_asus_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* port-A for surround (rear panel) - * according to codec#0 this is the HP jack - */ - { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */ - /* route front PCM to HP */ - { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 }, - /* port-B for mic-in (rear panel) with vref */ - { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-C for line-in (rear panel) */ - { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* port-D for Front */ - { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-E for HP out (front panel) */ - /* this has to be set to VREF80 */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* route front PCM to HP */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - /* port-F for mic-in (front panel) with vref */ - { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* port-G for CLFE (rear panel) */ - { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* port-H for side (rear panel) */ - { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* CD-in */ - { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* route front mic to ADC1*/ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute DAC0~3 & spdif out*/ - {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}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */ - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* hp used DAC 3 (Front) */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - { } -}; - -/* additional init verbs for ASUS laptops */ -static const struct hda_verb alc861_asus_laptop_init_verbs[] = { - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */ - { } -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc861_auto_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute DAC0~3 & spdif out*/ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Unmute Mixer 14 (mic) 1c (Line in)*/ - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* Unmute Stereo Mixer 15 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, - - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */ - - { } -}; - -static const struct hda_verb alc861_toshiba_init_verbs[] = { - {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc861_toshiba_automute(struct hda_codec *codec) -{ - unsigned int present = snd_hda_jack_detect(codec, 0x0f); - - snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, - HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); -} - -static void alc861_toshiba_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc861_toshiba_automute(codec); -} - -/* pcm configuration: identical with ALC880 */ -#define alc861_pcm_analog_playback alc880_pcm_analog_playback -#define alc861_pcm_analog_capture alc880_pcm_analog_capture -#define alc861_pcm_digital_playback alc880_pcm_digital_playback -#define alc861_pcm_digital_capture alc880_pcm_digital_capture - - -#define ALC861_DIGOUT_NID 0x07 - -static const struct hda_channel_mode alc861_8ch_modes[1] = { - { 8, NULL } -}; - -static const hda_nid_t alc861_dac_nids[4] = { - /* front, surround, clfe, side */ - 0x03, 0x06, 0x05, 0x04 -}; - -static const hda_nid_t alc660_dac_nids[3] = { - /* front, clfe, surround */ - 0x03, 0x05, 0x06 -}; - -static const hda_nid_t alc861_adc_nids[1] = { - /* ADC0-2 */ - 0x08, -}; - -static const struct hda_input_mux alc861_capture_source = { - .num_items = 5, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x3 }, - { "Line", 0x1 }, - { "CD", 0x4 }, - { "Mixer", 0x5 }, - }, -}; - -static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t mix, srcs[5]; - int i, j, num; - - if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) - return 0; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) - return 0; - for (i = 0; i < num; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, srcs[i])); - if (type != AC_WID_AUD_OUT) - continue; - for (j = 0; j < spec->multiout.num_dacs; j++) - if (spec->multiout.dac_nids[j] == srcs[i]) - break; - if (j >= spec->multiout.num_dacs) - return srcs[i]; - } - return 0; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc861_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t nid, dac; - - spec->multiout.dac_nids = spec->private_dac_nids; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - dac = alc861_look_for_dac(codec, nid); - if (!dac) - continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - return 0; -} - -static int __alc861_create_out_sw(struct hda_codec *codec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) -{ - return __add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); -} - -#define alc861_create_out_sw(codec, pfx, nid, chs) \ - __alc861_create_out_sw(codec, pfx, nid, 0, chs) - -/* add playback controls from the parsed DAC table */ -static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); - hda_nid_t nid; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - nid = spec->multiout.dac_nids[i]; - if (!nid) - continue; - if (!pfx && i == 2) { - /* Center/LFE */ - err = alc861_create_out_sw(codec, "Center", nid, 1); - if (err < 0) - return err; - err = alc861_create_out_sw(codec, "LFE", nid, 2); - if (err < 0) - return err; - } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } - err = __alc861_create_out_sw(codec, name, nid, index, 3); - if (err < 0) - return err; - } - } - return 0; -} - -static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int err; - hda_nid_t nid; - - if (!pin) - return 0; - - if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { - nid = alc861_look_for_dac(codec, pin); - if (nid) { - err = alc861_create_out_sw(codec, "Headphone", nid, 3); - if (err < 0) - return err; - spec->multiout.hp_nid = nid; - } - } - return 0; -} - -/* create playback/capture controls for input pins */ -static int alc861_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0); -} - -static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, - int pin_type, hda_nid_t dac) -{ - hda_nid_t mix, srcs[5]; - int i, num; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_get_connections(codec, nid, &mix, 1) != 1) - return; - num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); - if (num < 0) - return; - for (i = 0; i < num; i++) { - unsigned int mute; - if (srcs[i] == dac || srcs[i] == 0x15) - mute = AMP_IN_UNMUTE(i); - else - mute = AMP_IN_MUTE(i); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - mute); - } -} - -static void alc861_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc861_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc861_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->autocfg.hp_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.hp_pins[0], - PIN_HP, - spec->multiout.hp_nid); - if (spec->autocfg.speaker_outs) - alc861_auto_set_output_and_unmute(codec, - spec->autocfg.speaker_pins[0], - PIN_OUT, - spec->multiout.dac_nids[0]); -} - -static void alc861_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (nid >= 0x0c && nid <= 0x11) - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - } -} - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ static int alc861_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc861_auto_fill_dac_nids(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = alc861_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc861_auto_init_verbs); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - spec->adc_nids = alc861_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); - set_capture_mixer(codec); - - alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); - - return 1; -} - -/* additional initialization for auto-configuration model */ -static void alc861_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc861_auto_init_multi_out(codec); - alc861_auto_init_hp_out(codec); - alc861_auto_init_analog_input(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; + return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -16260,152 +4771,6 @@ static const struct hda_amp_list alc861_loopbacks[] = { #endif -/* - * configuration and preset - */ -static const char * const alc861_models[ALC861_MODEL_LAST] = { - [ALC861_3ST] = "3stack", - [ALC660_3ST] = "3stack-660", - [ALC861_3ST_DIG] = "3stack-dig", - [ALC861_6ST_DIG] = "6stack-dig", - [ALC861_UNIWILL_M31] = "uniwill-m31", - [ALC861_TOSHIBA] = "toshiba", - [ALC861_ASUS] = "asus", - [ALC861_ASUS_LAPTOP] = "asus-laptop", - [ALC861_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc861_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), - SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), - SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), - /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) - * Any other models that need this preset? - */ - /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ - SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), - SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), - /* FIXME: the below seems conflict */ - /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ - SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), - SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), - {} -}; - -static const struct alc_config_preset alc861_presets[] = { - [ALC861_3ST] = { - .mixers = { alc861_3ST_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_3ST_DIG] = { - .mixers = { alc861_base_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_6ST_DIG] = { - .mixers = { alc861_base_mixer }, - .init_verbs = { alc861_base_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes), - .channel_mode = alc861_8ch_modes, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC660_3ST] = { - .mixers = { alc861_3ST_mixer }, - .init_verbs = { alc861_threestack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660_dac_nids), - .dac_nids = alc660_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), - .channel_mode = alc861_threestack_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_UNIWILL_M31] = { - .mixers = { alc861_uniwill_m31_mixer }, - .init_verbs = { alc861_uniwill_m31_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes), - .channel_mode = alc861_uniwill_m31_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_TOSHIBA] = { - .mixers = { alc861_toshiba_mixer }, - .init_verbs = { alc861_base_init_verbs, - alc861_toshiba_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - .unsol_event = alc861_toshiba_unsol_event, - .init_hook = alc861_toshiba_automute, - }, - [ALC861_ASUS] = { - .mixers = { alc861_asus_mixer }, - .init_verbs = { alc861_asus_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861_asus_modes), - .channel_mode = alc861_asus_modes, - .need_dac_fix = 1, - .hp_nid = 0x06, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, - [ALC861_ASUS_LAPTOP] = { - .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer }, - .init_verbs = { alc861_asus_init_verbs, - alc861_asus_laptop_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861_dac_nids), - .dac_nids = alc861_dac_nids, - .dig_out_nid = ALC861_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), - .channel_mode = alc883_3ST_2ch_modes, - .need_dac_fix = 1, - .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), - .adc_nids = alc861_adc_nids, - .input_mux = &alc861_capture_source, - }, -}; - /* Pin config fixes */ enum { PINFIX_FSC_AMILO_PI1505, @@ -16427,6 +4792,12 @@ static const struct snd_pci_quirk alc861_fixup_tbl[] = { {} }; +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc861_quirks.c" +#endif + static int patch_alc861(struct hda_codec *codec) { struct alc_spec *spec; @@ -16439,61 +4810,67 @@ static int patch_alc861(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST, - alc861_models, - alc861_cfg_tbl); + spec->mixer_nid = 0x15; + + board_config = alc_board_config(codec, ALC861_MODEL_LAST, + alc861_models, alc861_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC861_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC861_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC861_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC861_3ST_DIG; } +#endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; - } - - if (board_config != ALC861_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc861_presets[board_config]); - spec->stream_analog_playback = &alc861_pcm_analog_playback; - spec->stream_analog_capture = &alc861_pcm_analog_capture; - - spec->stream_digital_playback = &alc861_pcm_digital_playback; - spec->stream_digital_capture = &alc861_pcm_digital_capture; + 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->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + } spec->vmaster_nid = 0x03; alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); codec->patch_ops = alc_patch_ops; - if (board_config == ALC861_AUTO) { - spec->init_hook = alc861_auto_init; + if (board_config == ALC_MODEL_AUTO) { + spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->power_hook = alc_power_eapd; #endif @@ -16513,871 +4890,15 @@ static int patch_alc861(struct hda_codec *codec) * * In addition, an independent DAC */ -#define ALC861VD_DIGOUT_NID 0x06 - -static const hda_nid_t alc861vd_dac_nids[4] = { - /* front, surr, clfe, side surr */ - 0x02, 0x03, 0x04, 0x05 -}; - -/* dac_nids for ALC660vd are in a different order - according to - * Realtek's driver. - * This should probably result in a different mixer for 6stack models - * of ALC660vd codecs, but for now there is only 3stack mixer - * - and it is the same as in 861vd. - * adc_nids in ALC660vd are (is) the same as in 861vd - */ -static const hda_nid_t alc660vd_dac_nids[3] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x04, 0x03 -}; - -static const hda_nid_t alc861vd_adc_nids[1] = { - /* ADC0 */ - 0x09, -}; - -static const hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ -static const struct hda_input_mux alc861vd_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc861vd_dallas_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; - -static const struct hda_input_mux alc861vd_hp_capture_source = { - .num_items = 2, - .items = { - { "Front Mic", 0x0 }, - { "ATAPI Mic", 0x1 }, - }, -}; - -/* - * 2ch mode - */ -static const struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc861vd_6stack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, 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 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static const struct hda_verb alc861vd_6stack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc861vd_6stack_modes[2] = { - { 6, alc861vd_6stack_ch6_init }, - { 8, alc861vd_6stack_ch8_init }, -}; - -static const struct snd_kcontrol_new alc861vd_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 */ -}; - -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ -static const struct snd_kcontrol_new alc861vd_6st_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, - HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 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", 0x05, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861vd_3st_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -static const struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/ - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - - { } /* end */ -}; - -/* Pin assignment: Speaker=0x14, HP = 0x15, - * Mic=0x18, Internal Mic = 0x19, CD = 0x1c, PC Beep = 0x1d - */ -static const struct snd_kcontrol_new alc861vd_dallas_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 */ -}; - -/* Pin assignment: Speaker=0x14, Line-out = 0x15, - * Front Mic=0x18, ATAPI Mic = 0x19, - */ -static const struct snd_kcontrol_new alc861vd_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - { } /* end */ -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc861vd_volume_init_verbs[] = { - /* - * Unmute ADC0 and set the default input to mic-in - */ - {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 - */ - /* 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)}, - - /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers (0x02 - 0x05) - */ - /* set vol=0 to output mixers */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, 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 alc861vd_3stack_init_verbs[] = { - /* - * 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}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - - /* 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}, - - { } -}; - -/* - * 6-stack pin configuration: - */ -static const struct hda_verb alc861vd_6stack_init_verbs[] = { - /* - * 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}, - {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}, - - { } -}; - -static const struct hda_verb alc861vd_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc660vd_eapd_verbs[] = { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc861vd_lenovo_unsol_verbs[] = { - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {} -}; - -static void alc861vd_lenovo_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc861vd_lenovo_init_hook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc88x_simple_mic_automute(codec); -} - -static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc88x_simple_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } -} - -static const struct hda_verb alc861vd_dallas_verbs[] = { - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, 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)}, - {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)}, - - {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_VREF50}, - {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_IN}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_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)}, - - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - - { } /* end */ -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc861vd_dallas_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc861vd_loopbacks alc880_loopbacks #endif -/* pcm configuration: identical with ALC880 */ -#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback -#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture -#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback -#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture - -/* - * configuration and preset - */ -static const char * const alc861vd_models[ALC861VD_MODEL_LAST] = { - [ALC660VD_3ST] = "3stack-660", - [ALC660VD_3ST_DIG] = "3stack-660-digout", - [ALC660VD_ASUS_V1S] = "asus-v1s", - [ALC861VD_3ST] = "3stack", - [ALC861VD_3ST_DIG] = "3stack-digout", - [ALC861VD_6ST_DIG] = "6stack-digout", - [ALC861VD_LENOVO] = "lenovo", - [ALC861VD_DALLAS] = "dallas", - [ALC861VD_HP] = "hp", - [ALC861VD_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc861vd_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), - SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), - /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ - SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), - SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), - SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), - /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ - SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), - SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), - {} -}; - -static const struct alc_config_preset alc861vd_presets[] = { - [ALC660VD_3ST] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC660VD_3ST_DIG] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_3ST] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_3ST_DIG] = { - .mixers = { alc861vd_3st_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_6ST_DIG] = { - .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_6stack_init_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), - .channel_mode = alc861vd_6stack_modes, - .input_mux = &alc861vd_capture_source, - }, - [ALC861VD_LENOVO] = { - .mixers = { alc861vd_lenovo_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs, - alc861vd_eapd_verbs, - alc861vd_lenovo_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - .unsol_event = alc861vd_lenovo_unsol_event, - .setup = alc861vd_lenovo_setup, - .init_hook = alc861vd_lenovo_init_hook, - }, - [ALC861VD_DALLAS] = { - .mixers = { alc861vd_dallas_mixer }, - .init_verbs = { alc861vd_dallas_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_dallas_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc861vd_dallas_setup, - .init_hook = alc_hp_automute, - }, - [ALC861VD_HP] = { - .mixers = { alc861vd_hp_mixer }, - .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), - .dac_nids = alc861vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_hp_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc861vd_dallas_setup, - .init_hook = alc_hp_automute, - }, - [ALC660VD_ASUS_V1S] = { - .mixers = { alc861vd_lenovo_mixer }, - .init_verbs = { alc861vd_volume_init_verbs, - alc861vd_3stack_init_verbs, - alc861vd_eapd_verbs, - alc861vd_lenovo_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), - .dac_nids = alc660vd_dac_nids, - .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), - .channel_mode = alc861vd_3stack_2ch_modes, - .input_mux = &alc861vd_capture_source, - .unsol_event = alc861vd_lenovo_unsol_event, - .setup = alc861vd_lenovo_setup, - .init_hook = alc861vd_lenovo_init_hook, - }, -}; - -/* - * BIOS auto configuration - */ -static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x22, 0); -} - - -static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, int dac_idx) -{ - alc_set_pin_output(codec, nid, pin_type); -} - -static void alc861vd_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - if (nid) - alc861vd_auto_set_output_and_unmute(codec, nid, - pin_type, i); - } -} - - -static void alc861vd_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front and use dac 0 */ - alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); -} - -#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc861vd_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC861VD_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } -} - -#define alc861vd_auto_init_input_src alc882_auto_init_input_src - -#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) -#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) - -/* add playback controls from the parsed DAC table */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - static const char * const chname[4] = { - "Front", "Surround", "CLFE", "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); - hda_nid_t nid_v, nid_s; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - if (!spec->multiout.dac_nids[i]) - continue; - nid_v = alc861vd_idx_to_mixer_vol( - alc880_dac_to_idx( - spec->multiout.dac_nids[i])); - nid_s = alc861vd_idx_to_mixer_switch( - alc880_dac_to_idx( - spec->multiout.dac_nids[i])); - - if (!pfx && i == 2) { - /* Center/LFE */ - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "Center", - HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "Center", - HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, - HDA_INPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - "LFE", - HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, - HDA_INPUT)); - if (err < 0) - return err; - } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, index, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, index, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, - HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -/* Based on ALC880 version. But ALC861VD has separate, - * different NIDs for mute/unmute switch and volume control */ -static int alc861vd_auto_create_extra_out(struct alc_spec *spec, - hda_nid_t pin, const char *pfx) -{ - hda_nid_t nid_v, nid_s; - int err; - - if (!pin) - return 0; - - if (alc880_is_fixed_pin(pin)) { - nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid_v; - else - spec->multiout.extra_out_nid[0] = nid_v; - /* control HP volume/switch on the output mixer amp */ - nid_v = alc861vd_idx_to_mixer_vol( - alc880_fixed_pin_idx(pin)); - nid_s = alc861vd_idx_to_mixer_switch( - alc880_fixed_pin_idx(pin)); - - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, - HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc880_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* parse the BIOS configuration and set up the alc_spec - * return 1 if successful, 0 if the proper config is not found, - * or a negative error code - * Based on ALC880 version - had to change it to override - * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ static int alc861vd_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc861vd_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.speaker_pins[0], - "Speaker"); - if (err < 0) - return err; - err = alc861vd_auto_create_extra_out(spec, - spec->autocfg.hp_pins[0], - "Headphone"); - if (err < 0) - return err; - err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc861vd_volume_init_verbs); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - return 1; -} - -/* additional initialization for auto-configuration model */ -static void alc861vd_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc861vd_auto_init_multi_out(codec); - alc861vd_auto_init_hp_out(codec); - alc861vd_auto_init_analog_input(codec); - alc861vd_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); } enum { @@ -17402,6 +4923,18 @@ static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { {} }; +static const struct hda_verb alc660vd_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc861vd_quirks.c" +#endif + static int patch_alc861vd(struct hda_codec *codec) { struct alc_spec *spec; @@ -17413,42 +4946,40 @@ static int patch_alc861vd(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, - alc861vd_models, - alc861vd_cfg_tbl); + spec->mixer_nid = 0x0b; - if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { + board_config = alc_board_config(codec, ALC861VD_MODEL_LAST, + alc861vd_models, alc861vd_cfg_tbl); + + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC861VD_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC861VD_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); } - if (board_config == ALC861VD_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC861VD_3ST; } +#endif } - err = snd_hda_attach_beep_device(codec, 0x23); - if (err < 0) { - alc_free(codec); - return err; - } - - if (board_config != ALC861VD_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc861vd_presets[board_config]); if (codec->vendor_id == 0x10ec0660) { @@ -17456,21 +4987,23 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); } - spec->stream_analog_playback = &alc861vd_pcm_analog_playback; - spec->stream_analog_capture = &alc861vd_pcm_analog_capture; + 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); + } - spec->stream_digital_playback = &alc861vd_pcm_digital_playback; - spec->stream_digital_capture = &alc861vd_pcm_digital_capture; + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); - if (!spec->adc_nids) { - spec->adc_nids = alc861vd_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + if (!spec->no_analog) { + err = snd_hda_attach_beep_device(codec, 0x23); + if (err < 0) { + alc_free(codec); + return err; + } + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc861vd_capsrc_nids; - - set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x02; @@ -17478,8 +5011,8 @@ static int patch_alc861vd(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; - if (board_config == ALC861VD_AUTO) - spec->init_hook = alc861vd_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) @@ -17500,1943 +5033,27 @@ static int patch_alc861vd(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#define ALC662_DIGOUT_NID 0x06 -#define ALC662_DIGIN_NID 0x0a - -static const hda_nid_t alc662_dac_nids[3] = { - /* front, rear, clfe */ - 0x02, 0x03, 0x04 -}; - -static const hda_nid_t alc272_dac_nids[2] = { - 0x02, 0x03 -}; - -static const hda_nid_t alc662_adc_nids[2] = { - /* ADC1-2 */ - 0x09, 0x08 -}; - -static const hda_nid_t alc272_adc_nids[1] = { - /* ADC1-2 */ - 0x08, -}; - -static const hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 }; -static const hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; - - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ -static const struct hda_input_mux alc662_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static const struct hda_input_mux alc662_lenovo_101e_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static const struct hda_input_mux alc663_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -#if 0 /* set to 1 for testing other input sources below */ -static const struct hda_input_mux alc272_nc10_capture_source = { - .num_items = 16, - .items = { - { "Autoselect Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "In-0x02", 0x2 }, - { "In-0x03", 0x3 }, - { "In-0x04", 0x4 }, - { "In-0x05", 0x5 }, - { "In-0x06", 0x6 }, - { "In-0x07", 0x7 }, - { "In-0x08", 0x8 }, - { "In-0x09", 0x9 }, - { "In-0x0a", 0x0a }, - { "In-0x0b", 0x0b }, - { "In-0x0c", 0x0c }, - { "In-0x0d", 0x0d }, - { "In-0x0e", 0x0e }, - { "In-0x0f", 0x0f }, - }, -}; -#endif - -/* - * 2ch mode - */ -static const struct hda_channel_mode alc662_3ST_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc662_3ST_ch2_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, 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 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc662_3ST_ch6_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc662_3ST_6ch_modes[2] = { - { 2, alc662_3ST_ch2_init }, - { 6, alc662_3ST_ch6_init }, -}; - -/* - * 2ch mode - */ -static const struct hda_verb alc662_sixstack_ch6_init[] = { - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static const struct hda_verb alc662_sixstack_ch8_init[] = { - { 0x16, 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 }, - { } /* end */ -}; - -static const struct hda_channel_mode alc662_5stack_modes[2] = { - { 2, alc662_sixstack_ch6_init }, - { 6, alc662_sixstack_ch8_init }, -}; - -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ - -static const struct snd_kcontrol_new alc662_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - - /*Input mixer control */ - HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, 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_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), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, 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_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), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, 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, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, 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 Boost Volume", 0x19, 0, 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 alc662_eeepc_ep20_mixer[] = { - ALC262_HIPPO_MASTER_SWITCH, - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, 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), - { } /* end */ -}; - -static const struct hda_bind_ctls alc663_asus_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc663_asus_one_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc663_m51va_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_bind_ctls alc663_asus_tree_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - - { } /* end */ -}; - -static const struct hda_bind_ctls alc663_asus_four_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc662_1bjd_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_bind_ctls alc663_asus_two_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc663_asus_two_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", - &alc663_asus_two_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc663_g71v_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - 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 alc663_g50v_mixer[] = { - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - 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), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc663_mode7_mixer[] = { - HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), - HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), - HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, 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 snd_kcontrol_new alc663_mode8_mixer[] = { - HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), - HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), - HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), - HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - { } /* end */ -}; - - -static const struct snd_kcontrol_new alc662_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 alc662_init_verbs[] = { - /* ADC: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - - {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)}, - - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* 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 mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - { } -}; - -static const struct hda_verb alc662_eapd_init_verbs[] = { - /* always trun on EAPD */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, - { } -}; - -static const struct hda_verb alc662_sue_init_verbs[] = { - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc662_eeepc_sue_init_verbs[] = { - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -/* Set Unsolicited Event*/ -static const struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_m51va_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_21jd_amic_init_verbs[] = { - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc662_1bjd_amic_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {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}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_15jd_amic_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = { - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {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, 0x01}, /* Headphone */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_g71v_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */ - /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */ - - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ - - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_g50v_init_verbs[] = { - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */ - - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc662_ecs_init_verbs[] = { - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc272_dell_zm1_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc272_dell_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_mode7_init_verbs[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, 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_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct hda_verb alc663_mode8_init_verbs[] = { - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, - {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {} -}; - -static const struct snd_kcontrol_new alc662_auto_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new alc272_auto_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { } /* end */ -}; - -static void alc662_lenovo_101e_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.line_out_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x15; - spec->automute = 1; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc662_eeepc_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc262_hippo1_setup(codec); - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -static void alc662_eeepc_ep20_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x1b; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc663_m51va_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; - spec->auto_mic = 1; -} - -/* ***************** Mode1 ******************************/ -static void alc663_mode1_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode2 ******************************/ -static void alc662_mode2_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode3 ******************************/ -static void alc663_mode3_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode4 ******************************/ -static void alc663_mode4_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute_mixer_nid[1] = 0x0e; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode5 ******************************/ -static void alc663_mode5_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x16; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute_mixer_nid[1] = 0x0e; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode6 ******************************/ -static void alc663_mode6_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute_mixer_nid[0] = 0x0c; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_MIXER; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode7 ******************************/ -static void alc663_mode7_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x19; - spec->int_mic.mux_idx = 1; - spec->auto_mic = 1; -} - -/* ***************** Mode8 ******************************/ -static void alc663_mode8_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.hp_pins[1] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[0] = 0x17; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; - spec->auto_mic = 1; -} - -static void alc663_g71v_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x21; - spec->autocfg.line_out_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x14; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; - spec->detect_line = 1; - spec->automute_lines = 1; - spec->ext_mic.pin = 0x18; - spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 9; - spec->auto_mic = 1; -} - -#define alc663_g50v_setup alc663_m51va_setup - -static const struct snd_kcontrol_new alc662_ecs_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - ALC262_HIPPO_MASTER_SWITCH, - - HDA_CODEC_VOLUME("Mic/LineIn Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT), - - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, 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 alc272_nc10_mixer[] = { - /* Master Playback automatically created from Speaker and Headphone */ - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT), - { } /* end */ -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc662_loopbacks alc880_loopbacks #endif - -/* pcm configuration: identical with ALC880 */ -#define alc662_pcm_analog_playback alc880_pcm_analog_playback -#define alc662_pcm_analog_capture alc880_pcm_analog_capture -#define alc662_pcm_digital_playback alc880_pcm_digital_playback -#define alc662_pcm_digital_capture alc880_pcm_digital_capture - -/* - * configuration and preset - */ -static const char * const alc662_models[ALC662_MODEL_LAST] = { - [ALC662_3ST_2ch_DIG] = "3stack-dig", - [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", - [ALC662_3ST_6ch] = "3stack-6ch", - [ALC662_5ST_DIG] = "5stack-dig", - [ALC662_LENOVO_101E] = "lenovo-101e", - [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", - [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", - [ALC662_ECS] = "ecs", - [ALC663_ASUS_M51VA] = "m51va", - [ALC663_ASUS_G71V] = "g71v", - [ALC663_ASUS_H13] = "h13", - [ALC663_ASUS_G50V] = "g50v", - [ALC663_ASUS_MODE1] = "asus-mode1", - [ALC662_ASUS_MODE2] = "asus-mode2", - [ALC663_ASUS_MODE3] = "asus-mode3", - [ALC663_ASUS_MODE4] = "asus-mode4", - [ALC663_ASUS_MODE5] = "asus-mode5", - [ALC663_ASUS_MODE6] = "asus-mode6", - [ALC663_ASUS_MODE7] = "asus-mode7", - [ALC663_ASUS_MODE8] = "asus-mode8", - [ALC272_DELL] = "dell", - [ALC272_DELL_ZM1] = "dell-zm1", - [ALC272_SAMSUNG_NC10] = "samsung-nc10", - [ALC662_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc662_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), - SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL), - SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), - /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/ - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), - /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/ - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), - SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), - SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), - SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), - SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), - SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), - SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", - ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", - ALC663_ASUS_H13), - SND_PCI_QUIRK(0x1991, 0x5628, "Ordissimo EVE", ALC662_LENOVO_101E), - {} -}; - -static const struct alc_config_preset alc662_presets[] = { - [ALC662_3ST_2ch_DIG] = { - .mixers = { alc662_3ST_2ch_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_capture_source, - }, - [ALC662_3ST_6ch_DIG] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc662_capture_source, - }, - [ALC662_3ST_6ch] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc662_capture_source, - }, - [ALC662_5ST_DIG] = { - .mixers = { alc662_base_mixer, alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .dig_in_nid = ALC662_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), - .channel_mode = alc662_5stack_modes, - .input_mux = &alc662_capture_source, - }, - [ALC662_LENOVO_101E] = { - .mixers = { alc662_lenovo_101e_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_lenovo_101e_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_lenovo_101e_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_EEEPC_P701] = { - .mixers = { alc662_eeepc_p701_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_eeepc_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_EEEPC_EP20] = { - .mixers = { alc662_eeepc_ep20_mixer, - alc662_chmode_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_eeepc_ep20_sue_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .input_mux = &alc662_lenovo_101e_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_ep20_setup, - .init_hook = alc_inithook, - }, - [ALC662_ECS] = { - .mixers = { alc662_ecs_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_ecs_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_eeepc_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_M51VA] = { - .mixers = { alc663_m51va_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_m51va_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_G71V] = { - .mixers = { alc663_g71v_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_g71v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_g71v_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_H13] = { - .mixers = { alc663_m51va_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_m51va_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .setup = alc663_m51va_setup, - .unsol_event = alc_sku_unsol_event, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_G50V] = { - .mixers = { alc663_g50v_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_g50v_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), - .channel_mode = alc662_3ST_6ch_modes, - .input_mux = &alc663_capture_source, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_g50v_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE1] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode1_setup, - .init_hook = alc_inithook, - }, - [ALC662_ASUS_MODE2] = { - .mixers = { alc662_1bjd_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc662_1bjd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc662_mode2_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE3] = { - .mixers = { alc663_two_hp_m1_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_two_hp_amic_m1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode3_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE4] = { - .mixers = { alc663_asus_21jd_clfe_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs}, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode4_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE5] = { - .mixers = { alc663_asus_15jd_clfe_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_15jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode5_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE6] = { - .mixers = { alc663_two_hp_m2_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_two_hp_amic_m2_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode6_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE7] = { - .mixers = { alc663_mode7_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_mode7_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode7_setup, - .init_hook = alc_inithook, - }, - [ALC663_ASUS_MODE8] = { - .mixers = { alc663_mode8_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_mode8_init_verbs }, - .num_dacs = ARRAY_SIZE(alc662_dac_nids), - .hp_nid = 0x03, - .dac_nids = alc662_dac_nids, - .dig_out_nid = ALC662_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode8_setup, - .init_hook = alc_inithook, - }, - [ALC272_DELL] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc272_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc272_dell_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .adc_nids = alc272_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), - .capsrc_nids = alc272_capsrc_nids, - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC272_DELL_ZM1] = { - .mixers = { alc663_m51va_mixer }, - .cap_mixer = alc662_auto_capture_mixer, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc272_dell_zm1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .adc_nids = alc662_adc_nids, - .num_adc_nids = 1, - .capsrc_nids = alc662_capsrc_nids, - .channel_mode = alc662_3ST_2ch_modes, - .unsol_event = alc_sku_unsol_event, - .setup = alc663_m51va_setup, - .init_hook = alc_inithook, - }, - [ALC272_SAMSUNG_NC10] = { - .mixers = { alc272_nc10_mixer }, - .init_verbs = { alc662_init_verbs, - alc662_eapd_init_verbs, - alc663_21jd_amic_init_verbs }, - .num_dacs = ARRAY_SIZE(alc272_dac_nids), - .dac_nids = alc272_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), - .channel_mode = alc662_3ST_2ch_modes, - /*.input_mux = &alc272_nc10_capture_source,*/ - .unsol_event = alc_sku_unsol_event, - .setup = alc663_mode4_setup, - .init_hook = alc_inithook, - }, -}; - - /* * BIOS auto configuration */ -/* convert from MIX nid to DAC */ -static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[5]; - int i, num; - - num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); - for (i = 0; i < num; i++) { - if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) - return list[i]; - } - return 0; -} - -/* go down to the selector widget before the mixer */ -static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, pin, srcs, - ARRAY_SIZE(srcs)); - if (num != 1 || - get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) - return pin; - return srcs[0]; -} - -/* get MIX nid connected to the given pin targeted to DAC */ -static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) - return mix[i]; - } - return 0; -} - -/* select the connection from pin to DAC if needed */ -static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - if (num < 2) - return 0; - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, i); - return 0; - } - } - return 0; -} - -/* 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, j, num; - - 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++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (!nid) - continue; - for (j = 0; j < spec->multiout.num_dacs; j++) - if (spec->multiout.dac_nids[j] == nid) - break; - if (j >= spec->multiout.num_dacs) - return nid; - } - return 0; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int alc662_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t dac; - - spec->multiout.dac_nids = spec->private_dac_nids; - for (i = 0; i < cfg->line_outs; i++) { - dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]); - if (!dac) - continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - return 0; -} - -static inline int __alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) -{ - return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); -} - -static inline int __alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, - hda_nid_t nid, int idx, unsigned int chs) -{ - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); -} - -#define alc662_add_vol_ctl(spec, pfx, nid, chs) \ - __alc662_add_vol_ctl(spec, pfx, nid, 0, chs) -#define alc662_add_sw_ctl(spec, pfx, nid, chs) \ - __alc662_add_sw_ctl(spec, pfx, nid, 0, chs) -#define alc662_add_stereo_vol(spec, pfx, nid) \ - alc662_add_vol_ctl(spec, pfx, nid, 3) -#define alc662_add_stereo_sw(spec, pfx, nid) \ - alc662_add_sw_ctl(spec, pfx, nid, 3) - -/* add playback controls from the parsed DAC table */ -static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - const char *pfx = alc_get_line_out_pfx(spec, true); - hda_nid_t nid, mix, pin; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - nid = spec->multiout.dac_nids[i]; - if (!nid) - continue; - if (i >= cfg->line_outs) - pin = spec->multi_io[i - 1].pin; - else - pin = cfg->line_out_pins[i]; - mix = alc_auto_dac_to_mix(codec, pin, nid); - if (!mix) - continue; - if (!pfx && i == 2) { - /* Center/LFE */ - err = alc662_add_vol_ctl(spec, "Center", nid, 1); - if (err < 0) - return err; - err = alc662_add_vol_ctl(spec, "LFE", nid, 2); - if (err < 0) - return err; - err = alc662_add_sw_ctl(spec, "Center", mix, 1); - if (err < 0) - return err; - err = alc662_add_sw_ctl(spec, "LFE", mix, 2); - if (err < 0) - return err; - } else { - const char *name = pfx; - int index = i; - if (!name) { - name = chname[i]; - index = 0; - } - err = __alc662_add_vol_ctl(spec, name, nid, index, 3); - if (err < 0) - return err; - err = __alc662_add_sw_ctl(spec, name, mix, index, 3); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -/* return DAC nid if any new DAC is assigned */ -static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - const char *pfx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid, mix; - int err; - - if (!pin) - return 0; - nid = alc_auto_look_for_dac(codec, pin); - if (!nid) { - /* the corresponding DAC is already occupied */ - if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) - return 0; /* no way */ - /* create a switch only */ - return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - } - - mix = alc_auto_dac_to_mix(codec, pin, nid); - if (!mix) - return 0; - err = alc662_add_vol_ctl(spec, pfx, nid, 3); - if (err < 0) - return err; - err = alc662_add_sw_ctl(spec, pfx, mix, 3); - if (err < 0) - return err; - return nid; -} - -/* create playback/capture controls for input pins */ -#define alc662_auto_create_input_ctls \ - alc882_auto_create_input_ctls - -static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - hda_nid_t dac) -{ - int i, num; - hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - - alc_set_pin_output(codec, nid, pin_type); - num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) - continue; - /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - snd_hda_codec_write(codec, srcs[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, srcs[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - return; - } -} - -static void alc662_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - int i; - - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - alc662_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} - -static void alc662_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.hp_pins[0]; - if (pin) - alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, - spec->multiout.hp_nid); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, - spec->multiout.extra_out_nid[0]); -} - -#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc662_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (nid != ALC662_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } - } -} - -#define alc662_auto_init_input_src alc882_auto_init_input_src - -/* - * multi-io helper - */ -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, num_pins = 0; - - 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; - 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) - continue; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - continue; - dac = alc_auto_look_for_dac(codec, nid); - if (!dac) - 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->multiout.num_dacs = 1; - if (num_pins < 2) - return 0; - return num_pins; -} - -static int alc_auto_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; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); - return 0; -} - -static int alc_auto_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; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; - return 0; -} - -static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - - if (!spec->multi_io[idx].ctl_in) - spec->multi_io[idx].ctl_in = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (output) { - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); - } else { - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->multi_io[idx].ctl_in); - } - return 0; -} - -static int alc_auto_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 i, ch; - - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) - return 0; - spec->ext_channel_count = (ch + 1) * 2; - for (i = 0; i < spec->multi_ios; i++) - alc_set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = spec->ext_channel_count; - return 1; -} - -static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_auto_ch_mode_info, - .get = alc_auto_ch_mode_get, - .put = alc_auto_ch_mode_put, -}; - -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int location, defcfg; - int num_pins; - - if (cfg->line_outs != 1 || - cfg->line_out_type != AUTO_PIN_LINE_OUT) - return 0; - - 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); - if (num_pins > 0) { - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec); - if (!knew) - return -ENOMEM; - *knew = alc_auto_channel_mode_enum; - knew->name = kstrdup("Channel Mode", GFP_KERNEL); - if (!knew->name) - return -ENOMEM; - - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; - } - return 0; -} - static int alc662_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc662_ignore); - if (err < 0) - return err; - if (!spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - - err = alc662_auto_fill_dac_nids(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = alc662_auto_create_extra_out(codec, - spec->autocfg.speaker_pins[0], - "Speaker"); - if (err < 0) - return err; - if (err) - spec->multiout.extra_out_nid[0] = err; - err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], - "Headphone"); - if (err < 0) - return err; - if (err) - spec->multiout.hp_nid = err; - err = alc662_auto_create_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - alc_auto_parse_digital(codec); - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - spec->num_mux_defs = 1; - spec->input_mux = &spec->private_imux[0]; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; + static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + const hda_nid_t *ssids; if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21); + ssids = alc663_ssids; else - alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); - - return 1; -} - -/* additional initialization for auto-configuration model */ -static void alc662_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc662_auto_init_multi_out(codec); - alc662_auto_init_hp_out(codec); - alc662_auto_init_analog_input(codec); - alc662_auto_init_input_src(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + ssids = alc662_ssids; + return alc_parse_auto_config(codec, alc662_ignore, ssids); } static void alc272_fixup_mario(struct hda_codec *codec, @@ -19459,6 +5076,7 @@ enum { ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, ALC662_FIXUP_SKU_IGNORE, + ALC662_FIXUP_HP_RP5800, }; static const struct alc_fixup alc662_fixups[] = { @@ -19491,12 +5109,22 @@ static const struct alc_fixup alc662_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, + [ALC662_FIXUP_HP_RP5800] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0221201f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), 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(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), @@ -19510,6 +5138,12 @@ static const struct alc_model_fixup alc662_fixup_models[] = { }; +/* + */ +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc662_quirks.c" +#endif + static int patch_alc662(struct hda_codec *codec) { struct alc_spec *spec; @@ -19522,6 +5156,8 @@ static int patch_alc662(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + alc_auto_parse_customize_define(codec); alc_fix_pll_init(codec, 0x20, 0x04, 15); @@ -19536,16 +5172,15 @@ static int patch_alc662(struct hda_codec *codec) else if (coef == 0x4011) alc_codec_rename(codec, "ALC656"); - board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, - alc662_models, - alc662_cfg_tbl); + board_config = alc_board_config(codec, ALC662_MODEL_LAST, + alc662_models, alc662_cfg_tbl); if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC662_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC662_AUTO) { + if (board_config == ALC_MODEL_AUTO) { alc_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -19554,42 +5189,35 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC662_3ST_2ch_DIG; } +#endif } - if (has_cdefine_beep(codec)) { - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - } - - if (board_config != ALC662_AUTO) + if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc662_presets[board_config]); - spec->stream_analog_playback = &alc662_pcm_analog_playback; - spec->stream_analog_capture = &alc662_pcm_analog_capture; - - spec->stream_digital_playback = &alc662_pcm_digital_playback; - spec->stream_digital_capture = &alc662_pcm_digital_capture; - - if (!spec->adc_nids) { - spec->adc_nids = alc662_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + 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->capsrc_nids) - spec->capsrc_nids = alc662_capsrc_nids; - if (!spec->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); - if (has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { + err = snd_hda_attach_beep_device(codec, 0x1); + if (err < 0) { + alc_free(codec); + return err; + } switch (codec->vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -19609,8 +5237,8 @@ static int patch_alc662(struct hda_codec *codec) alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); codec->patch_ops = alc_patch_ops; - if (board_config == ALC662_AUTO) - spec->init_hook = alc662_auto_init; + if (board_config == ALC_MODEL_AUTO) + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; alc_init_jacks(codec); @@ -19652,389 +5280,17 @@ static int patch_alc899(struct hda_codec *codec) /* * ALC680 support */ -#define ALC680_DIGIN_NID ALC880_DIGIN_NID -#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID -#define alc680_modes alc260_modes - -static const hda_nid_t alc680_dac_nids[3] = { - /* Lout1, Lout2, hp */ - 0x02, 0x03, 0x04 -}; - -static const hda_nid_t alc680_adc_nids[3] = { - /* ADC0-2 */ - /* DMIC, MIC, Line-in*/ - 0x07, 0x08, 0x09 -}; - -/* - * Analog capture ADC cgange - */ -static void alc680_rec_autoswitch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int pin_found = 0; - int type_found = AUTO_PIN_LAST; - hda_nid_t nid; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - if (!is_jack_detectable(codec, nid)) - continue; - if (snd_hda_jack_detect(codec, nid)) { - if (cfg->inputs[i].type < type_found) { - type_found = cfg->inputs[i].type; - pin_found = nid; - } - } - } - - nid = 0x07; - if (pin_found) - snd_hda_get_connections(codec, pin_found, &nid, 1); - - if (nid != spec->cur_adc) - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = nid; - snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); -} -static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - spec->cur_adc = 0x07; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - - alc680_rec_autoswitch(codec); - return 0; -} - -static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, 0x07); - snd_hda_codec_cleanup_stream(codec, 0x08); - snd_hda_codec_cleanup_stream(codec, 0x09); - return 0; -} - -static const struct hda_pcm_stream alc680_pcm_analog_auto_capture = { - .substreams = 1, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .prepare = alc680_capture_pcm_prepare, - .cleanup = alc680_capture_pcm_cleanup - }, -}; - -static const struct snd_kcontrol_new alc680_base_mixer[] = { - /* output mixer control */ - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x12, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost Volume", 0x19, 0, HDA_INPUT), - { } -}; - -static const struct hda_bind_ctls alc680_bind_cap_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct hda_bind_ctls alc680_bind_cap_switch = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), - 0 - }, -}; - -static const struct snd_kcontrol_new alc680_master_capture_mixer[] = { - HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol), - HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch), - { } /* end */ -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb alc680_init_verbs[] = { - {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}, - - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, - - { } -}; - -/* toggle speaker-output according to the hp-jack state */ -static void alc680_base_setup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - spec->autocfg.hp_pins[0] = 0x16; - spec->autocfg.speaker_pins[0] = 0x14; - spec->autocfg.speaker_pins[1] = 0x15; - spec->autocfg.num_inputs = 2; - spec->autocfg.inputs[0].pin = 0x18; - spec->autocfg.inputs[0].type = AUTO_PIN_MIC; - spec->autocfg.inputs[1].pin = 0x19; - spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN; - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_AMP; -} - -static void alc680_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc_hp_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc680_rec_autoswitch(codec); -} - -static void alc680_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec); - alc680_rec_autoswitch(codec); -} - -/* create input playback/capture controls for the given pin */ -static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid, - const char *ctlname, int idx) -{ - hda_nid_t dac; - int err; - - switch (nid) { - case 0x14: - dac = 0x02; - break; - case 0x15: - dac = 0x03; - break; - case 0x16: - dac = 0x04; - break; - default: - return 0; - } - if (spec->multiout.dac_nids[0] != dac && - spec->multiout.dac_nids[1] != dac) { - err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, - HDA_COMPOSE_AMP_VAL(dac, 3, idx, - HDA_OUTPUT)); - if (err < 0) - return err; - - err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); - - if (err < 0) - return err; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - hda_nid_t nid; - int err; - - spec->multiout.dac_nids = spec->private_dac_nids; - - nid = cfg->line_out_pins[0]; - if (nid) { - const char *name; - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - name = "Speaker"; - else - name = "Front"; - err = alc680_new_analog_output(spec, nid, name, 0); - if (err < 0) - return err; - } - - nid = cfg->speaker_pins[0]; - if (nid) { - err = alc680_new_analog_output(spec, nid, "Speaker", 0); - if (err < 0) - return err; - } - nid = cfg->hp_pins[0]; - if (nid) { - err = alc680_new_analog_output(spec, nid, "Headphone", 0); - if (err < 0) - return err; - } - - return 0; -} - -static void alc680_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type) -{ - alc_set_pin_output(codec, nid, pin_type); -} - -static void alc680_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.line_out_pins[0]; - if (nid) { - int pin_type = get_pin_type(spec->autocfg.line_out_type); - alc680_auto_set_output_and_unmute(codec, nid, pin_type); - } -} - -static void alc680_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.hp_pins[0]; - if (pin) - alc680_auto_set_output_and_unmute(codec, pin, PIN_HP); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT); -} - -/* pcm configuration: identical with ALC880 */ -#define alc680_pcm_analog_playback alc880_pcm_analog_playback -#define alc680_pcm_analog_capture alc880_pcm_analog_capture -#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture -#define alc680_pcm_digital_playback alc880_pcm_digital_playback -#define alc680_pcm_digital_capture alc880_pcm_digital_capture - -/* - * BIOS auto configuration - */ static int alc680_parse_auto_config(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int err; - static const hda_nid_t alc680_ignore[] = { 0 }; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - alc680_ignore); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs) { - if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = 2; - - dig_only: - /* digital only support output */ - alc_auto_parse_digital(codec); - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); - - add_verb(spec, alc680_init_verbs); - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - return 1; -} - -#define alc680_auto_init_analog_input alc882_auto_init_analog_input - -/* init callback for auto-configuration model -- overriding the default init */ -static void alc680_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc680_auto_init_multi_out(codec); - alc680_auto_init_hp_out(codec); - alc680_auto_init_analog_input(codec); - alc_auto_init_digital(codec); - if (spec->unsol_event) - alc_inithook(codec); + return alc_parse_auto_config(codec, NULL, NULL); } /* - * configuration and preset */ -static const char * const alc680_models[ALC680_MODEL_LAST] = { - [ALC680_BASE] = "base", - [ALC680_AUTO] = "auto", -}; - -static const struct snd_pci_quirk alc680_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE), - {} -}; - -static const struct alc_config_preset alc680_presets[] = { - [ALC680_BASE] = { - .mixers = { alc680_base_mixer }, - .cap_mixer = alc680_master_capture_mixer, - .init_verbs = { alc680_init_verbs }, - .num_dacs = ARRAY_SIZE(alc680_dac_nids), - .dac_nids = alc680_dac_nids, - .dig_out_nid = ALC680_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc680_modes), - .channel_mode = alc680_modes, - .unsol_event = alc680_unsol_event, - .setup = alc680_base_setup, - .init_hook = alc680_inithook, - - }, -}; +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS +#include "alc680_quirks.c" +#endif static int patch_alc680(struct hda_codec *codec) { @@ -20048,51 +5304,55 @@ static int patch_alc680(struct hda_codec *codec) codec->spec = spec; - board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST, - alc680_models, - alc680_cfg_tbl); + /* ALC680 has no aa-loopback mixer */ + + board_config = alc_board_config(codec, ALC680_MODEL_LAST, + alc680_models, alc680_cfg_tbl); - if (board_config < 0 || board_config >= ALC680_MODEL_LAST) { + if (board_config < 0) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); - board_config = ALC680_AUTO; + board_config = ALC_MODEL_AUTO; } - if (board_config == ALC680_AUTO) { + if (board_config == ALC_MODEL_AUTO) { /* automatic parse from the BIOS config */ err = alc680_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; - } else if (!err) { + } +#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 = ALC680_BASE; } +#endif } - if (board_config != ALC680_AUTO) + if (board_config != ALC_MODEL_AUTO) { setup_preset(codec, &alc680_presets[board_config]); +#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS + spec->stream_analog_capture = &alc680_pcm_analog_auto_capture; +#endif + } - spec->stream_analog_playback = &alc680_pcm_analog_playback; - spec->stream_analog_capture = &alc680_pcm_analog_auto_capture; - spec->stream_digital_playback = &alc680_pcm_digital_playback; - spec->stream_digital_capture = &alc680_pcm_digital_capture; - - if (!spec->adc_nids) { - spec->adc_nids = alc680_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids); + 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->cap_mixer) + if (!spec->no_analog && !spec->cap_mixer) set_capture_mixer(codec); spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; - if (board_config == ALC680_AUTO) - spec->init_hook = alc680_auto_init; + if (board_config == ALC_MODEL_AUTO) + 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 7f81cc2274f3..aa376b59c006 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -95,6 +95,7 @@ enum { STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, STAC_92HD83XXX_HP, + STAC_92HD83XXX_HP_cNB11_INTQUAD, STAC_HP_DV7_4000, STAC_92HD83XXX_MODELS }; @@ -212,6 +213,7 @@ struct sigmatel_spec { unsigned int gpio_mute; unsigned int gpio_led; unsigned int gpio_led_polarity; + unsigned int vref_led; /* stream */ unsigned int stream_delay; @@ -671,6 +673,30 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } +static int stac_vrefout_set(struct hda_codec *codec, + hda_nid_t nid, unsigned int new_vref) +{ + int error, pinctl; + + snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref); + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + if (pinctl < 0) + return pinctl; + + pinctl &= 0xff; + pinctl &= ~AC_PINCTL_VREFEN; + pinctl |= (new_vref & AC_PINCTL_VREFEN); + + error = snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + if (error < 0) + return error; + + return 1; +} + static unsigned int stac92xx_vref_set(struct hda_codec *codec, hda_nid_t nid, unsigned int new_vref) { @@ -1112,7 +1138,9 @@ static int stac92xx_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, @@ -1634,10 +1662,17 @@ static const unsigned int hp_dv7_4000_pin_configs[10] = { 0x40f000f0, 0x40f000f0, }; +static const unsigned int hp_cNB11_intquad_pin_configs[10] = { + 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, + 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, + 0x40f000f0, 0x40f000f0, +}; + static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, [STAC_DELL_S14] = dell_s14_pin_configs, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, }; @@ -1647,6 +1682,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", [STAC_92HD83XXX_HP] = "hp", + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", [STAC_HP_DV7_4000] = "hp-dv7-4000", }; @@ -1659,7 +1695,47 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, - "HP", STAC_92HD83XXX_HP), + "HP", STAC_92HD83XXX_HP), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), {} /* terminator */ }; @@ -3406,30 +3482,9 @@ static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, return 0; } -static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; - - if (!(get_wcaps(codec, mux) & AC_WCAP_CONN_LIST)) - return -1; - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - - for (i = 0; i < nums; i++) { - unsigned int wid_caps = get_wcaps(codec, conn[i]); - unsigned int wid_type = get_wcaps_type(wid_caps); - - if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX) - if (get_connection_index(codec, conn[i], nid) >= 0) - return i; - } - return -1; -} +/* look for NID recursively */ +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 1) /* create a volume assigned to the given pin (only if supported) */ /* return 1 if the volume control is created */ @@ -4039,6 +4094,8 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, { unsigned int gpiostate, gpiomask, gpiodir; + snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DATA, 0); gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); @@ -4228,10 +4285,12 @@ static void stac_store_hints(struct hda_codec *codec) spec->eapd_switch = val; get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity); if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - if (spec->gpio_led_polarity) - spec->gpio_data |= spec->gpio_led; + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + if (spec->gpio_led_polarity) + spec->gpio_data |= spec->gpio_led; + } } } @@ -4401,11 +4460,26 @@ static void stac92xx_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void stac92xx_shutup_pins(struct hda_codec *codec) +{ + unsigned int i, def_conf; + + if (codec->bus->shutdown) + return; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } +} + static void stac92xx_shutup(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - snd_hda_shutup_pins(codec); + stac92xx_shutup_pins(codec); if (spec->eapd_mask) stac_gpio_set(codec, spec->gpio_mask, @@ -4803,10 +4877,11 @@ static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity) if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%d", + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &spec->gpio_led_polarity, &spec->gpio_led) == 2) { - spec->gpio_led = 1 << spec->gpio_led; + if (spec->gpio_led < 4) + spec->gpio_led = 1 << spec->gpio_led; return 1; } if (sscanf(dev->name, "HP_Mute_LED_%d", @@ -4904,7 +4979,7 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #define stac927x_proc_hook NULL #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int stac92xx_resume(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4920,29 +4995,81 @@ static int stac92xx_resume(struct hda_codec *codec) stac_issue_unsol_event(codec, spec->autocfg.line_out_pins[0]); } + return 0; +} + +static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +{ + stac92xx_shutup(codec); + return 0; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int stac92xx_pre_resume(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + /* sync mute LED */ - if (spec->gpio_led) - hda_call_check_power_status(codec, 0x01); + if (spec->gpio_led) { + if (spec->gpio_led <= 8) { + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } else { + stac_vrefout_set(codec, + spec->gpio_led, spec->vref_led); + } + } + return 0; +} + +static int stac92xx_post_suspend(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + if (spec->gpio_led > 8) { + /* with vref-out pin used for mute led control + * codec AFG is prevented from D3 state, but on + * system suspend it can (and should) be used + */ + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } return 0; } +static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + unsigned int afg_power_state = power_state; + struct sigmatel_spec *spec = codec->spec; + + if (power_state == AC_PWRST_D3) { + if (spec->gpio_led > 8) { + /* with vref-out pin used for mute led control + * codec AFG is prevented from D3 state + */ + afg_power_state = AC_PWRST_D1; + } + /* this delay seems necessary to avoid click noise at power-down */ + msleep(100); + } + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + afg_power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} + /* - * using power check for controlling mute led of HP notebooks - * check for mute state only on Speakers (nid = 0x10) - * - * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise - * the LED is NOT working properly ! - * - * Changed name to reflect that it now works for any designated - * model, not just HP HDX. + * For this feature CONFIG_SND_HDA_POWER_SAVE is needed + * as mute LED state is updated in check_power_status hook */ - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int stac92xx_hp_check_power_status(struct hda_codec *codec, - hda_nid_t nid) +static int stac92xx_update_led_status(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i, muted = 1; + int i, num_ext_dacs, muted = 1; + unsigned int muted_lvl, notmtd_lvl; + hda_nid_t nid; + + if (!spec->gpio_led) + return 0; for (i = 0; i < spec->multiout.num_dacs; i++) { nid = spec->multiout.dac_nids[i]; @@ -4952,27 +5079,58 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, break; } } - 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; + 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->gpio_led <= 8) { + if (muted) + spec->gpio_data &= ~spec->gpio_led; /* orange */ + else + spec->gpio_data |= spec->gpio_led; /* white */ - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + 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_HIZ : AC_PINCTL_VREF_GRD; + muted_lvl = spec->gpio_led_polarity ? + AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ; + spec->vref_led = muted ? muted_lvl : notmtd_lvl; + stac_vrefout_set(codec, spec->gpio_led, spec->vref_led); + } return 0; } -#endif -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +/* + * 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_shutup(codec); + stac92xx_update_led_status(codec); + return 0; } -#endif +#endif /* CONFIG_SND_HDA_POWER_SAVE */ +#endif /* CONFIG_PM */ static const struct hda_codec_ops stac92xx_patch_ops = { .build_controls = stac92xx_build_controls, @@ -4980,7 +5138,7 @@ static const struct hda_codec_ops stac92xx_patch_ops = { .init = stac92xx_init, .free = stac92xx_free, .unsol_event = stac92xx_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif @@ -5496,12 +5654,19 @@ again: #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - /* register check_power_status callback. */ + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + codec->patch_ops.post_suspend = + stac92xx_post_suspend; + } + codec->patch_ops.pre_resume = stac92xx_pre_resume; codec->patch_ops.check_power_status = - stac92xx_hp_check_power_status; + stac92xx_check_power_status; } #endif @@ -5824,12 +5989,19 @@ again: #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - /* register check_power_status callback. */ + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + codec->patch_ops.post_suspend = + stac92xx_post_suspend; + } + codec->patch_ops.pre_resume = stac92xx_pre_resume; codec->patch_ops.check_power_status = - stac92xx_hp_check_power_status; + stac92xx_check_power_status; } #endif diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f43bb0eaed8b..84d8798bf33a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,36 +54,10 @@ #include "hda_codec.h" #include "hda_local.h" -#define NID_MAPPING (-1) - -/* amp values */ -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) - /* Pin Widget NID */ -#define VT1708_HP_NID 0x13 -#define VT1708_DIGOUT_NID 0x14 -#define VT1708_DIGIN_NID 0x16 -#define VT1708_DIGIN_PIN 0x26 #define VT1708_HP_PIN_NID 0x20 #define VT1708_CD_PIN_NID 0x24 -#define VT1709_HP_DAC_NID 0x28 -#define VT1709_DIGOUT_NID 0x13 -#define VT1709_DIGIN_NID 0x17 -#define VT1709_DIGIN_PIN 0x25 - -#define VT1708B_HP_NID 0x25 -#define VT1708B_DIGOUT_NID 0x12 -#define VT1708B_DIGIN_NID 0x15 -#define VT1708B_DIGIN_PIN 0x21 - -#define VT1708S_HP_NID 0x25 -#define VT1708S_DIGOUT_NID 0x12 - -#define VT1702_HP_NID 0x17 -#define VT1702_DIGOUT_NID 0x11 - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, @@ -107,6 +81,39 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) +#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; +}; + +/* input-path */ +struct via_input { + hda_nid_t pin; /* input-pin or aa-mix */ + int adc_idx; /* ADC index to be used */ + int mux_idx; /* MUX index (if any) */ + const char *label; /* input-source label */ +}; + +#define VIA_MAX_ADCS 3 + +enum { + STREAM_MULTI_OUT = (1 << 0), + STREAM_INDEP_HP = (1 << 1), +}; + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -115,28 +122,66 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; - char *stream_name_analog; + char stream_name_analog[32]; + char stream_name_hp[32]; const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; - char *stream_name_digital; + char stream_name_digital[32]; const struct hda_pcm_stream *stream_digital_playback; const struct hda_pcm_stream *stream_digital_capture; /* playback */ struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; + hda_nid_t hp_dac_nid; + hda_nid_t speaker_dac_nid; + int hp_indep_shared; /* indep HP-DAC is shared with side ch */ + int opened_streams; /* STREAM_* bits */ + int active_streams; /* STREAM_* bits */ + int aamix_mode; /* loopback is enabled for output-path? */ + + /* Output-paths: + * There are different output-paths depending on the setup. + * out_path, hp_path and speaker_path are primary paths. If both + * direct DAC and aa-loopback routes are available, these contain + * the former paths. Meanwhile *_mix_path contain the paths with + * loopback mixer. (Since the loopback is only for front channel, + * no out_mix_path for surround channels.) + * The HP output has another path, hp_indep_path, which is used in + * the independent-HP mode. + */ + struct nid_path out_path[HDA_SIDE + 1]; + struct nid_path out_mix_path; + struct nid_path hp_path; + struct nid_path hp_mix_path; + struct nid_path hp_indep_path; + struct nid_path speaker_path; + struct nid_path speaker_mix_path; /* capture */ unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; + hda_nid_t adc_nids[VIA_MAX_ADCS]; + hda_nid_t mux_nids[VIA_MAX_ADCS]; + hda_nid_t aa_mix_nid; hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; + bool dyn_adc_switch; + int num_inputs; + struct via_input inputs[AUTO_CFG_MAX_INS + 1]; + unsigned int cur_mux[VIA_MAX_ADCS]; + + /* dynamic DAC switching */ + unsigned int cur_dac_stream_tag; + unsigned int cur_dac_format; + unsigned int cur_hp_stream_tag; + unsigned int cur_hp_format; + + /* dynamic ADC switching */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; /* PCM information */ struct hda_pcm pcm_rec[3]; @@ -144,28 +189,38 @@ struct via_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct snd_array kctls; - struct hda_input_mux private_imux[2]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; /* HP mode source */ - const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; - unsigned int hp_independent_mode_index; - unsigned int smart51_enabled; unsigned int dmic_enabled; + unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; + /* smart51 setup */ + unsigned int smart51_nums; + hda_nid_t smart51_pins[2]; + int smart51_idxs[2]; + const char *smart51_labels[2]; + unsigned int smart51_enabled; + /* work to check hp jack state */ struct hda_codec *codec; struct delayed_work vt1708_hp_work; - int vt1708_jack_detectect; + int vt1708_jack_detect; int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; -#endif + int num_loopbacks; + struct hda_amp_list loopback_list[8]; + + /* bind capture-volume */ + struct hda_bind_ctls *bind_cap_vol; + struct hda_bind_ctls *bind_cap_sw; + + struct mutex config_mutex; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -177,6 +232,7 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; + mutex_init(&spec->config_mutex); codec->spec = spec; spec->codec = codec; spec->codec_type = get_codec_type(codec); @@ -237,33 +293,23 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_JACK_EVENT 0x20 #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 -#define VIA_MONO_EVENT 0x03 -#define VIA_SPEAKER_EVENT 0x04 -#define VIA_BIND_HP_EVENT 0x05 +#define VIA_LINE_EVENT 0x03 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, - VIA_CTL_WIDGET_BIND_PIN_MUTE, }; -enum { - AUTO_SEQ_FRONT = 0, - AUTO_SEQ_SURROUND, - AUTO_SEQ_CENLFE, - AUTO_SEQ_SIDE -}; - -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); -static int is_aa_path_mute(struct hda_codec *codec); +static void analog_low_current_mode(struct hda_codec *codec); +static bool is_aa_path_mute(struct hda_codec *codec); static void vt1708_start_hp_work(struct via_spec *spec) { if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); + !spec->vt1708_jack_detect); if (!delayed_work_pending(&spec->vt1708_hp_work)) schedule_delayed_work(&spec->vt1708_hp_work, msecs_to_jiffies(100)); @@ -277,7 +323,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) && !is_aa_path_mute(spec->codec)) return; snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); + !spec->vt1708_jack_detect); cancel_delayed_work_sync(&spec->vt1708_hp_work); } @@ -295,7 +341,7 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); set_widgets_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + analog_low_current_mode(snd_kcontrol_chip(kcontrol)); if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { if (is_aa_path_mute(codec)) vt1708_start_hp_work(codec->spec); @@ -315,168 +361,44 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, .put = analog_input_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } -static void via_hp_bind_automute(struct hda_codec *codec); - -static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int i; - int change = 0; - - long *valp = ucontrol->value.integer.value; - int lmute, rmute; - if (strstr(kcontrol->id.name, "Switch") == NULL) { - snd_printd("Invalid control!\n"); - return change; - } - change = snd_hda_mixer_amp_switch_put(kcontrol, - ucontrol); - /* Get mute value */ - lmute = *valp ? 0 : HDA_AMP_MUTE; - valp++; - rmute = *valp ? 0 : HDA_AMP_MUTE; - - /* Set hp pins */ - if (!spec->hp_independent_mode) { - for (i = 0; i < spec->autocfg.hp_outs; i++) { - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - } - } - - if (!lmute && !rmute) { - /* Line Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* unmute */ - via_hp_bind_automute(codec); - - } else { - if (lmute) { - /* Mute all left channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - } - if (rmute) { - /* mute all right channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - } - } - return change; -} - -#define BIND_PIN_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = bind_pin_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - static const struct snd_kcontrol_new via_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, - BIND_PIN_MUTE, }; -static const hda_nid_t vt1708_adc_nids[2] = { - /* ADC1-2 */ - 0x15, 0x27 -}; - -static const hda_nid_t vt1709_adc_nids[3] = { - /* ADC1-2 */ - 0x14, 0x15, 0x16 -}; -static const hda_nid_t vt1708B_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1708S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1702_adc_nids[3] = { - /* ADC1-2 */ - 0x12, 0x20, 0x1F -}; - -static const hda_nid_t vt1718S_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static const hda_nid_t vt1716S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt2002P_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static const hda_nid_t vt1812_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; +/* add dynamic controls */ +static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, + const struct snd_kcontrol_new *tmpl, + const char *name) +{ + struct snd_kcontrol_new *knew; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *tmpl; + if (!name) + name = tmpl->name; + if (name) { + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + } + return knew; +} -/* add dynamic controls */ static int __via_add_control(struct via_spec *spec, int type, const char *name, int idx, unsigned long val) { struct snd_kcontrol_new *knew; - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); + knew = __via_clone_ctl(spec, &via_control_templates[type], name); if (!knew) return -ENOMEM; - *knew = via_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return -ENOMEM; + knew->index = idx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; @@ -486,21 +408,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name, #define via_add_control(spec, type, name, val) \ __via_add_control(spec, type, name, 0, val) -static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, - const struct snd_kcontrol_new *tmpl) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *tmpl; - knew->name = kstrdup(tmpl->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} +#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) static void via_free_kctls(struct hda_codec *codec) { @@ -535,58 +443,208 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname, return 0; } -static void via_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) + +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int mask) +{ + unsigned int caps; + if (!nid) + return false; + caps = get_wcaps(codec, nid); + if (dir == HDA_INPUT) + caps &= AC_WCAP_IN_AMP; + else + caps &= AC_WCAP_OUT_AMP; + if (!caps) + return false; + if (query_amp_caps(codec, nid, dir) & mask) + return true; + return false; +} + +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) + +/* enable/disable the output-route mixers */ +static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, + hda_nid_t mix_nid, int idx, bool enable) +{ + int i, num, val; + + if (!path) + return; + num = snd_hda_get_conn_list(codec, mix_nid, NULL); + for (i = 0; i < num; i++) { + if (i == idx) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); + snd_hda_codec_write(codec, mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) +{ + struct via_spec *spec = codec->spec; + int i; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (!force && (dst == spec->aa_mix_nid)) + continue; + if (have_mute(codec, dst, HDA_INPUT)) + activate_output_mix(codec, path, dst, idx, enable); + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } +} + +/* set the given pin as output */ +static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, + int pin_type) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + if (!pin) + return; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, + if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type) +{ + unsigned int caps; + hda_nid_t pin; + + if (!path->depth) + return; + pin = path->path[path->depth - 1]; + + init_output_pin(codec, pin, pin_type); + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + if (caps & AC_AMPCAP_MUTE) { + unsigned int val; + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE | val); + } + activate_output_path(codec, path, true, true); /* force on */ +} static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + struct nid_path *path; int i; - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { + path = &spec->out_path[i]; + if (!i && spec->aamix_mode && spec->out_mix_path.depth) + path = &spec->out_mix_path; + via_auto_init_output(codec, path, PIN_OUT); + } +} + +/* deactivate the inactive headphone-paths */ +static void deactivate_hp_paths(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int shared = spec->hp_indep_shared; + + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); + if (shared) + activate_output_path(codec, &spec->out_path[shared], + false, false); + } else if (spec->aamix_mode || !spec->hp_path.depth) { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_path, false, false); + } else { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); } } static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - hda_nid_t pin; - int i; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + if (!spec->hp_path.depth) { + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + return; + } + deactivate_hp_paths(codec); + if (spec->hp_independent_mode) + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); + else if (spec->aamix_mode) + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + else + via_auto_init_output(codec, &spec->hp_path, PIN_HP); +} + +static void via_auto_init_speaker_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->autocfg.speaker_outs) + return; + if (!spec->speaker_path.depth) { + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); + return; + } + if (!spec->aamix_mode) { + activate_output_path(codec, &spec->speaker_mix_path, + false, false); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); + } else { + activate_output_path(codec, &spec->speaker_path, false, false); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); +static void via_hp_automute(struct hda_codec *codec); static void via_auto_init_analog_input(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; unsigned int ctl; - int i; + int i, num_conns; + + /* init ADCs */ + for (i = 0; i < spec->num_adc_nids; i++) { + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + /* init pins */ for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - if (spec->smart51_enabled && is_smart51_pins(spec, nid)) + if (spec->smart51_enabled && is_smart51_pins(codec, nid)) ctl = PIN_OUT; else if (cfg->inputs[i].type == AUTO_PIN_MIC) ctl = PIN_VREF50; @@ -595,6 +653,32 @@ static void via_auto_init_analog_input(struct hda_codec *codec) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); } + + /* init input-src */ + for (i = 0; i < spec->num_adc_nids; i++) { + int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + if (spec->mux_nids[adc_idx]) { + int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + if (spec->dyn_adc_switch) + break; /* only one input-src */ + } + + /* init aa-mixer */ + if (!spec->aa_mix_nid) + return; + num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn)); + for (i = 0; i < num_conns; i++) { + unsigned int caps = get_wcaps(codec, conn[i]); + if (get_wcaps_type(caps) == AC_WID_PIN) + snd_hda_codec_write(codec, spec->aa_mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, @@ -605,9 +689,13 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned no_presence = (def_conf & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - unsigned present = snd_hda_jack_detect(codec, nid); struct via_spec *spec = codec->spec; - if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + unsigned present = 0; + + no_presence |= spec->no_pin_power_ctl; + if (!no_presence) + present = snd_hda_jack_detect(codec, nid); + if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -618,124 +706,139 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } -/* - * input MUX handling - */ -static int via_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); + static const char * const texts[] = { + "Disabled", "Enabled" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; return 0; } -static int via_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - int ret; - - if (!spec->mux_nids[adc_idx]) - return -EINVAL; - /* switch to D0 beofre change index */ - if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) - snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + unsigned int val = !ucontrol->value.enumerated.item[0]; - ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->mux_nids[adc_idx], - &spec->cur_mux[adc_idx]); - /* update jack power state */ + if (val == spec->no_pin_power_ctl) + return 0; + spec->no_pin_power_ctl = val; set_widgets_power_state(codec); - - return ret; + return 1; } +static const struct snd_kcontrol_new via_pin_power_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Dynamic Power-Control", + .info = via_pin_power_ctl_info, + .get = via_pin_power_ctl_get, + .put = via_pin_power_ctl_put, +}; + + static int via_independent_hp_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->hp_mux, uinfo); + static const char * const texts[] = { "OFF", "ON" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } static int via_independent_hp_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; - unsigned int pinsel; - - /* use !! to translate conn sel 2 for VT1718S */ - pinsel = !!snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); - ucontrol->value.enumerated.item[0] = pinsel; + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; return 0; } -static void activate_ctl(struct hda_codec *codec, const char *name, int active) -{ - struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); - if (ctl) { - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access |= active - ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); - } -} - -static hda_nid_t side_mute_channel(struct via_spec *spec) +/* adjust spec->multiout setup according to the current flags */ +static void setup_playback_multi_pcm(struct via_spec *spec) { - switch (spec->codec_type) { - case VT1708: return 0x1b; - case VT1709_10CH: return 0x29; - case VT1708B_8CH: /* fall thru */ - case VT1708S: return 0x27; - case VT2002P: return 0x19; - case VT1802: return 0x15; - case VT1812: return 0x15; - default: return 0; + const struct auto_pin_cfg *cfg = &spec->autocfg; + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.hp_nid = 0; + if (!spec->hp_independent_mode) { + if (!spec->hp_indep_shared) + spec->multiout.hp_nid = spec->hp_dac_nid; + } else { + if (spec->hp_indep_shared) + spec->multiout.num_dacs = cfg->line_outs - 1; } } -static int update_side_mute_status(struct hda_codec *codec) +/* update DAC setups according to indep-HP switch; + * this function is called only when indep-HP is modified + */ +static void switch_indep_hp_dacs(struct hda_codec *codec) { - /* mute side channel */ struct via_spec *spec = codec->spec; - unsigned int parm; - hda_nid_t sw3 = side_mute_channel(spec); + int shared = spec->hp_indep_shared; + hda_nid_t shared_dac, hp_dac; - if (sw3) { - if (VT2002P_COMPATIBLE(spec)) - parm = spec->hp_independent_mode ? - AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); - else - parm = spec->hp_independent_mode ? - AMP_OUT_MUTE : AMP_OUT_UNMUTE; - snd_hda_codec_write(codec, sw3, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); + if (!spec->opened_streams) + return; + + shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; + hp_dac = spec->hp_dac_nid; + if (spec->hp_independent_mode) { + /* switch to indep-HP mode */ + if (spec->active_streams & STREAM_MULTI_OUT) { + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); + } + if (spec->active_streams & STREAM_INDEP_HP) + snd_hda_codec_setup_stream(codec, hp_dac, + spec->cur_hp_stream_tag, 0, + spec->cur_hp_format); + } else { + /* back to HP or shared-DAC */ + if (spec->active_streams & STREAM_INDEP_HP) + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + if (spec->active_streams & STREAM_MULTI_OUT) { + hda_nid_t dac; + int ch; + if (shared_dac) { /* reset mutli-ch DAC */ + dac = shared_dac; + ch = shared * 2; + } else { /* reset HP DAC */ + dac = hp_dac; + ch = 0; + } + snd_hda_codec_setup_stream(codec, dac, + spec->cur_dac_stream_tag, ch, + spec->cur_dac_format); + } } - return 0; + setup_playback_multi_pcm(spec); } static int via_independent_hp_put(struct snd_kcontrol *kcontrol, @@ -743,66 +846,46 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - unsigned int pinsel = ucontrol->value.enumerated.item[0]; - unsigned int parm0, parm1; - /* Get Independent Mode index of headphone pin widget */ - spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel - ? 1 : 0; - if (spec->codec_type == VT1718S) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); - /* Set correct mute switch for MW3 */ - parm0 = spec->hp_independent_mode ? - AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0); - parm1 = spec->hp_independent_mode ? - AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm0); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm1); - } - else - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); + int cur, shared; - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); - if (spec->multiout.hp_nid && spec->multiout.hp_nid - != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, - 0, 0, 0); - - update_side_mute_status(codec); - /* update HP volume/swtich active state */ - if (spec->codec_type == VT1708S - || spec->codec_type == VT1702 - || spec->codec_type == VT1718S - || spec->codec_type == VT1716S - || VT2002P_COMPATIBLE(spec)) { - activate_ctl(codec, "Headphone Playback Volume", - spec->hp_independent_mode); - activate_ctl(codec, "Headphone Playback Switch", - spec->hp_independent_mode); + mutex_lock(&spec->config_mutex); + cur = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode == cur) { + mutex_unlock(&spec->config_mutex); + return 0; } + spec->hp_independent_mode = cur; + shared = spec->hp_indep_shared; + deactivate_hp_paths(codec); + if (cur) + activate_output_path(codec, &spec->hp_indep_path, true, false); + else { + if (shared) + activate_output_path(codec, &spec->out_path[shared], + true, false); + if (spec->aamix_mode || !spec->hp_path.depth) + activate_output_path(codec, &spec->hp_mix_path, + true, false); + else + activate_output_path(codec, &spec->hp_path, + true, false); + } + + switch_indep_hp_dacs(codec); + mutex_unlock(&spec->config_mutex); + /* update jack power state */ set_widgets_power_state(codec); - return 0; + via_hp_automute(codec); + return 1; } -static const struct snd_kcontrol_new via_hp_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = via_independent_hp_info, - .get = via_independent_hp_get, - .put = via_independent_hp_put, - }, - { - .iface = NID_MAPPING, - .name = "Independent HP", - }, +static const struct snd_kcontrol_new via_hp_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = via_independent_hp_info, + .get = via_independent_hp_get, + .put = via_independent_hp_put, }; static int via_hp_build(struct hda_codec *codec) @@ -810,61 +893,28 @@ static int via_hp_build(struct hda_codec *codec) struct via_spec *spec = codec->spec; struct snd_kcontrol_new *knew; hda_nid_t nid; - int nums; - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - break; - case VT2002P: - case VT1802: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } - - if (spec->codec_type != VT1708) { - nums = snd_hda_get_connections(codec, nid, - conn, HDA_MAX_CONNECTIONS); - if (nums <= 1) - return 0; - } - - knew = via_clone_control(spec, &via_hp_mixer[0]); + nid = spec->autocfg.hp_pins[0]; + knew = via_clone_control(spec, &via_hp_mixer); if (knew == NULL) return -ENOMEM; knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - knew->private_value = nid; - - nid = side_mute_channel(spec); - if (nid) { - knew = via_clone_control(spec, &via_hp_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = nid; - } return 0; } static void notify_aa_path_ctls(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; int i; - struct snd_ctl_elem_id id; - const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; - struct snd_kcontrol *ctl; - - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - for (i = 0; i < ARRAY_SIZE(labels); i++) { - sprintf(id.name, "%s Playback Volume", labels[i]); + + for (i = 0; i < spec->smart51_nums; i++) { + struct snd_kcontrol *ctl; + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); ctl = snd_hda_find_mixer_ctl(codec, id.name); if (ctl) snd_ctl_notify(codec->bus->card, @@ -876,66 +926,28 @@ static void notify_aa_path_ctls(struct hda_codec *codec) static void mute_aa_path(struct hda_codec *codec, int mute) { struct via_spec *spec = codec->spec; - hda_nid_t nid_mixer; - int start_idx; - int end_idx; + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; int i; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708: - nid_mixer = 0x17; - start_idx = 2; - end_idx = 4; - break; - case VT1709_10CH: - case VT1709_6CH: - nid_mixer = 0x18; - start_idx = 2; - end_idx = 4; - break; - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - nid_mixer = 0x16; - start_idx = 2; - end_idx = 4; - break; - case VT1718S: - nid_mixer = 0x21; - start_idx = 1; - end_idx = 3; - break; - default: - return; - } + /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, + for (i = 0; i < spec->smart51_nums; i++) { + if (spec->smart51_idxs[i] < 0) + continue; + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, + HDA_INPUT, spec->smart51_idxs[i], HDA_AMP_MUTE, val); } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) { - const struct auto_pin_cfg *cfg = &spec->autocfg; + struct via_spec *spec = codec->spec; int i; - for (i = 0; i < cfg->num_inputs; i++) { - if (pin == cfg->inputs[i].pin) - return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; - } - return 0; -} - -static int via_smart51_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; + for (i = 0; i < spec->smart51_nums; i++) + if (spec->smart51_pins[i] == pin) + return true; + return false; } static int via_smart51_get(struct snd_kcontrol *kcontrol, @@ -943,23 +955,8 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int on = 1; - int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* ignore FMic for independent HP */ - if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) - on = 0; - } - *ucontrol->value.integer.value = on; + *ucontrol->value.integer.value = spec->smart51_enabled; return 0; } @@ -968,21 +965,14 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; int out_in = *ucontrol->value.integer.value ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; + for (i = 0; i < spec->smart51_nums; i++) { + hda_nid_t nid = spec->smart51_pins[i]; unsigned int parm; - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* don't retask FMic for independent HP */ - parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); @@ -994,171 +984,59 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } - if (spec->codec_type == VT1718S) { - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_UNMUTE); - } - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - if (spec->codec_type == VT1708S - || spec->codec_type == VT1716S) { - /* input = index 1 (AOW3) */ - snd_hda_codec_write( - codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, 1); - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, - 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); - } - } } spec->smart51_enabled = *ucontrol->value.integer.value; set_widgets_power_state(codec); return 1; } -static const struct snd_kcontrol_new via_smart51_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Smart 5.1", - .count = 1, - .info = via_smart51_info, - .get = via_smart51_get, - .put = via_smart51_put, - }, - { - .iface = NID_MAPPING, - .name = "Smart 5.1", - } +static const struct snd_kcontrol_new via_smart51_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = via_smart51_get, + .put = via_smart51_put, }; -static int via_smart51_build(struct via_spec *spec) +static int via_smart51_build(struct hda_codec *codec) { - struct snd_kcontrol_new *knew; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; + struct via_spec *spec = codec->spec; - if (!cfg) + if (!spec->smart51_nums) return 0; - if (cfg->line_outs > 2) - return 0; - - knew = via_clone_control(spec, &via_smart51_mixer[0]); - if (knew == NULL) + if (!via_clone_control(spec, &via_smart51_mixer)) return -ENOMEM; - - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { - knew = via_clone_control(spec, &via_smart51_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = nid; - break; - } - } - return 0; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -/* check AA path's mute statue */ -static int is_aa_path_mute(struct hda_codec *codec) +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) { - int mute = 1; - hda_nid_t nid_mixer; - int start_idx; - int end_idx; - int i; struct via_spec *spec = codec->spec; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - nid_mixer = 0x16; - start_idx = 2; - end_idx = 4; - break; - case VT1702: - nid_mixer = 0x1a; - start_idx = 1; - end_idx = 3; - break; - case VT1718S: - nid_mixer = 0x21; - start_idx = 1; - end_idx = 3; - break; - case VT2002P: - case VT1812: - case VT1802: - nid_mixer = 0x21; - start_idx = 0; - end_idx = 2; - break; - default: - return 0; - } - /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - unsigned int con_list = snd_hda_codec_read( - codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); - int shift = 8 * (i % 4); - hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; - unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); - if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { - /* check mute status while the pin is connected */ - int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, - HDA_INPUT, i) >> 7; - int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, - HDA_INPUT, i) >> 7; - if (!mute_l || !mute_r) { - mute = 0; - break; - } + const struct hda_amp_list *p; + int i, ch, v; + + for (i = 0; i < spec->num_loopbacks; i++) { + p = &spec->loopback_list[i]; + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; } } - return mute; + return true; } /* enter/exit analog low-current mode */ -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +static void analog_low_current_mode(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - static int saved_stream_idle = 1; /* saved stream idle status */ - int enable = is_aa_path_mute(codec); - unsigned int verb = 0; - unsigned int parm = 0; + bool enable; + unsigned int verb, parm; - if (stream_idle == -1) /* stream status did not change */ - enable = enable && saved_stream_idle; - else { - enable = enable && stream_idle; - saved_stream_idle = stream_idle; - } + enable = is_aa_path_mute(codec) && (spec->opened_streams != 0); /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -1193,119 +1071,69 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) /* * generic initialization of ADC, input mixers and output mixers */ -static const struct hda_verb vt1708_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x27, 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 - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers (0x19 - 0x1b) - */ - /* set vol=0 to output mixers */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input MW0 to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1708_init_verbs[] = { /* power down jack detect function */ {0x1, 0xf81, 0x1}, { } }; -static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, +static void set_stream_open(struct hda_codec *codec, int bit, bool active) +{ + struct via_spec *spec = codec->spec; + + if (active) + spec->opened_streams |= bit; + else + spec->opened_streams &= ~bit; + analog_low_current_mode(codec); +} + +static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - analog_low_current_mode(codec, idle); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); + const struct auto_pin_cfg *cfg = &spec->autocfg; + int err; + + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + set_stream_open(codec, STREAM_MULTI_OUT, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); + if (err < 0) { + set_stream_open(codec, STREAM_MULTI_OUT, false); + return err; + } + return 0; } -static void playback_multi_pcm_prep_0(struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_MULTI_OUT, false); + return 0; +} + +static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - int chs = substream->runtime->channels; - int i; - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { - if (chs == 2 && - snd_hda_is_supported_format(codec, mout->dig_out_nid, - format) && - !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { - mout->dig_out_used = HDA_DIG_ANALOG_DUP; - /* turn off SPDIF once; otherwise the IEC958 bits won't - * be updated */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & - ~AC_DIG1_ENABLE & 0xff); - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - stream_tag, 0, format); - /* turn on again (if needed) */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); - } else { - mout->dig_out_used = 0; - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - } - } - mutex_unlock(&codec->spdif_mutex); - - /* front */ - snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, - 0, format); - - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] - && !spec->hp_independent_mode) - /* headphone out will just decode front left/right (stereo) */ - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, - 0, format); - - /* extra outputs copied from front */ - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); - - /* surrounds */ - for (i = 1; i < mout->num_dacs; i++) { - if (chs >= (i + 1) * 2) /* independent out */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - i * 2, format); - else /* copy front */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - 0, format); - } + if (snd_BUG_ON(!spec->hp_dac_nid)) + return -EINVAL; + set_stream_open(codec, STREAM_INDEP_HP, true); + return 0; +} + +static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_INDEP_HP, false); + return 0; } static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -1315,18 +1143,36 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - if (substream->number == 0) - playback_multi_pcm_prep_0(codec, stream_tag, format, - substream); - else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - stream_tag, 0, format); - } + mutex_lock(&spec->config_mutex); + setup_playback_multi_pcm(spec); + snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); + /* remember for dynamic DAC switch with indep-HP */ + spec->active_streams |= STREAM_MULTI_OUT; + spec->cur_dac_stream_tag = stream_tag; + spec->cur_dac_format = format; + mutex_unlock(&spec->config_mutex); + vt1708_start_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, + stream_tag, 0, format); + spec->active_streams |= STREAM_INDEP_HP; + spec->cur_hp_stream_tag = stream_tag; + spec->cur_hp_format = format; + mutex_unlock(&spec->config_mutex); vt1708_start_hp_work(spec); return 0; } @@ -1336,37 +1182,26 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - const hda_nid_t *nids = mout->dac_nids; - int i; - if (substream->number == 0) { - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); - - if (mout->hp_nid && !spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); - - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && - mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); - } else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); - } + mutex_lock(&spec->config_mutex); + snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + spec->active_streams &= ~STREAM_MULTI_OUT; + mutex_unlock(&spec->config_mutex); + vt1708_stop_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); + spec->active_streams &= ~STREAM_INDEP_HP; + mutex_unlock(&spec->config_mutex); vt1708_stop_hp_work(spec); return 0; } @@ -1435,47 +1270,127 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } -static const struct hda_pcm_stream vt1708_pcm_analog_playback = { - .substreams = 2, +/* analog capture with dynamic ADC switching */ +static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; + + mutex_lock(&spec->config_mutex); + spec->cur_adc = spec->adc_nids[adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + mutex_unlock(&spec->config_mutex); + return 0; +} + +static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + mutex_unlock(&spec->config_mutex); + return 0; +} + +/* re-setup the stream if running; called from input-src put */ +static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[cur].adc_idx; + hda_nid_t adc = spec->adc_nids[adc_idx]; + bool ret = false; + + mutex_lock(&spec->config_mutex); + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + ret = true; + } + mutex_unlock(&spec->config_mutex); + return ret; +} + +static const struct hda_pcm_stream via_pcm_analog_playback = { + .substreams = 1, .channels_min = 2, .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ .ops = { - .open = via_playback_pcm_open, + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, }; +static const struct hda_pcm_stream via_pcm_hp_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_hp_pcm_open, + .close = via_playback_hp_pcm_close, + .prepare = via_playback_hp_pcm_prepare, + .cleanup = via_playback_hp_pcm_cleanup + }, +}; + static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 2, + .substreams = 1, .channels_min = 2, .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ /* We got noisy outputs on the right channel on VT1708 when * 24bit samples are used. Until any workaround is found, * disable the 24bit format, so far. */ .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { - .open = via_playback_pcm_open, + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup }, }; -static const struct hda_pcm_stream vt1708_pcm_analog_capture = { - .substreams = 2, +static const struct hda_pcm_stream via_pcm_analog_capture = { + .substreams = 1, /* will be changed in via_build_pcms() */ .channels_min = 2, .channels_max = 2, - .nid = 0x15, /* NID to query formats and rates */ + /* NID is set in via_build_pcms */ .ops = { .prepare = via_capture_pcm_prepare, .cleanup = via_capture_pcm_cleanup }, }; -static const struct hda_pcm_stream vt1708_pcm_digital_playback = { +static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_dyn_adc_capture_pcm_prepare, + .cleanup = via_dyn_adc_capture_pcm_cleanup, + }, +}; + +static const struct hda_pcm_stream via_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -1488,19 +1403,47 @@ static const struct hda_pcm_stream vt1708_pcm_digital_playback = { }, }; -static const struct hda_pcm_stream vt1708_pcm_digital_capture = { +static const struct hda_pcm_stream via_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, }; +/* + * 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", + NULL, +}; + static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; struct snd_kcontrol *kctl; - const struct snd_kcontrol_new *knew; int err, i; + if (spec->set_widgets_power_state) + if (!via_clone_control(spec, &via_pin_power_ctl_enum)) + return -ENOMEM; + for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) @@ -1509,6 +1452,7 @@ static int via_build_controls(struct hda_codec *codec) if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, spec->multiout.dig_out_nid); if (err < 0) return err; @@ -1524,6 +1468,23 @@ static int via_build_controls(struct hda_codec *codec) return err; } + /* 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); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, via_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, via_slave_sws); + if (err < 0) + return err; + } + /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { @@ -1532,22 +1493,9 @@ static int via_build_controls(struct hda_codec *codec) return err; } - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - err = snd_hda_add_nid(codec, kctl, 0, - knew->subdevice); - } - } - /* init power states */ set_widgets_power_state(codec); - analog_low_current_mode(codec, 1); + analog_low_current_mode(codec); via_free_kctls(codec); /* no longer needed */ return 0; @@ -1561,36 +1509,71 @@ static int via_build_pcms(struct hda_codec *codec) codec->num_pcms = 1; codec->pcm_info = info; + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; + + if (!spec->stream_analog_playback) + spec->stream_analog_playback = &via_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_analog_playback); + *spec->stream_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; + if (!spec->stream_analog_capture) { + if (spec->dyn_adc_switch) + spec->stream_analog_capture = + &via_pcm_dyn_adc_analog_capture; + else + spec->stream_analog_capture = &via_pcm_analog_capture; + } + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + if (!spec->dyn_adc_switch) + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids; + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; info++; + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); info->name = spec->stream_name_digital; info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { + if (!spec->stream_digital_playback) + spec->stream_digital_playback = + &via_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_digital_playback); + *spec->stream_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; } if (spec->dig_in_nid) { + if (!spec->stream_digital_capture) + spec->stream_digital_capture = + &via_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *(spec->stream_digital_capture); + *spec->stream_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } } + if (spec->hp_dac_nid) { + codec->num_pcms++; + info++; + snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), + "%s HP", codec->chip_name); + info->name = spec->stream_name_hp; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->hp_dac_nid; + } return 0; } @@ -1603,57 +1586,62 @@ static void via_free(struct hda_codec *codec) via_free_kctls(codec); vt1708_stop_hp_work(spec); - kfree(codec->spec); + kfree(spec->bind_cap_vol); + kfree(spec->bind_cap_sw); + kfree(spec); } -/* mute internal speaker if HP is plugged */ -static void via_hp_automute(struct hda_codec *codec) +/* mute/unmute outputs */ +static void toggle_output_mutes(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool mute) { - unsigned int present = 0; - struct via_spec *spec = codec->spec; - - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; - /* auto mute */ - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Front Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); + int i; + for (i = 0; i < num_pins; i++) { + unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (parm & AC_PINCTL_IN_EN) + continue; + if (mute) + parm &= ~AC_PINCTL_OUT_EN; + else + parm |= AC_PINCTL_OUT_EN; + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, parm); } } -/* mute mono out if HP or Line out is plugged */ -static void via_mono_automute(struct hda_codec *codec) +/* mute internal speaker if line-out is plugged */ +static void via_line_automute(struct hda_codec *codec, int present) { - unsigned int hp_present, lineout_present; struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1716S) + if (!spec->autocfg.speaker_outs) return; - - lineout_present = snd_hda_jack_detect(codec, + if (!present) + present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + present); +} - /* Mute Mono Out if Line Out is plugged */ - if (lineout_present) { - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); - return; - } +/* mute internal speaker if HP is plugged */ +static void via_hp_automute(struct hda_codec *codec) +{ + int present = 0; + int nums; + struct via_spec *spec = codec->spec; - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - if (!spec->hp_independent_mode) - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, - hp_present ? HDA_AMP_MUTE : 0); + if (spec->smart51_enabled) + nums = spec->autocfg.line_outs + spec->smart51_nums; + else + nums = spec->autocfg.line_outs; + toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); + + via_line_automute(codec, present); } static void via_gpio_control(struct hda_codec *codec) @@ -1678,9 +1666,9 @@ static void via_gpio_control(struct hda_codec *codec) if (gpio_data == 0x02) { /* unmute line out */ - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - + snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); if (vol_counter & 0x20) { /* decrease volume */ if (vol > master_vol) @@ -1697,73 +1685,12 @@ static void via_gpio_control(struct hda_codec *codec) } } else if (!(gpio_data & 0x02)) { /* mute line out */ - snd_hda_codec_amp_stereo(codec, - spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_MUTE); - } -} - -/* mute Internal-Speaker if HP is plugged */ -static void via_speaker_automute(struct hda_codec *codec) -{ - unsigned int hp_present; - struct via_spec *spec = codec->spec; - - if (!VT2002P_COMPATIBLE(spec)) - return; - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Speaker Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); - } -} - -/* mute line-out and internal speaker if HP is plugged */ -static void via_hp_bind_automute(struct hda_codec *codec) -{ - /* use long instead of int below just to avoid an internal compiler - * error with gcc 4.0.x - */ - unsigned long hp_present, present = 0; - struct via_spec *spec = codec->spec; - int i; - - if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) - return; - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); - - if (!spec->hp_independent_mode) { - /* Mute Line-Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); - if (hp_present) - present = hp_present; + snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + 0); } - /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } - /* unsolicited event for jack sensing */ static void via_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1775,46 +1702,13 @@ static void via_unsol_event(struct hda_codec *codec, res &= ~VIA_JACK_EVENT; - if (res == VIA_HP_EVENT) + if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT) via_hp_automute(codec); else if (res == VIA_GPIO_EVENT) via_gpio_control(codec); - else if (res == VIA_MONO_EVENT) - via_mono_automute(codec); - else if (res == VIA_SPEAKER_EVENT) - via_speaker_automute(codec); - else if (res == VIA_BIND_HP_EVENT) - via_hp_bind_automute(codec); -} - -static int via_init(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - for (i = 0; i < spec->num_iverbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - - /* Lydia Add for EAPD enable */ - if (!spec->dig_in_nid) { /* No Digital In connection */ - if (spec->dig_in_pin) { - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); - } - } else /* enable SPDIF-input pin */ - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); - - /* assign slave outs */ - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; - - return 0; } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int via_suspend(struct hda_codec *codec, pm_message_t state) { struct via_spec *spec = codec->spec; @@ -1833,12 +1727,16 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* */ + +static int via_init(struct hda_codec *codec); + static const struct hda_codec_ops via_patch_ops = { .build_controls = via_build_controls, .build_pcms = via_build_pcms, .init = via_init, .free = via_free, -#ifdef SND_HDA_NEEDS_RESUME + .unsol_event = via_unsol_event, +#ifdef CONFIG_PM .suspend = via_suspend, #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -1846,237 +1744,835 @@ static const struct hda_codec_ops via_patch_ops = { #endif }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) { + struct via_spec *spec = codec->spec; int i; - hda_nid_t nid; - spec->multiout.num_dacs = cfg->line_outs; + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == dac) + return false; + } + if (spec->hp_dac_nid == dac) + return false; + return true; +} + +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) +{ + struct via_spec *spec = codec->spec; + hda_nid_t conn[8]; + int i, nums; - spec->multiout.dac_nids = spec->private_dac_nids; + if (nid == spec->aa_mix_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } - for (i = 0; i < 4; i++) { + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { + /* aa-mix is requested but not included? */ + if (!(spec->aa_mix_nid && with_aa_mix == 1)) + goto found; + } + } + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT) + continue; + if (__parse_output_path(codec, conn[i], target_dac, + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; +} + +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { + path->path[path->depth] = nid; + path->depth++; + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); + return true; + } + return false; +} + +static int via_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, dac_num; + hda_nid_t nid; + + spec->multiout.dac_nids = spec->private_dac_nids; + dac_num = 0; + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = 0; nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x13; - break; - } + if (!nid) + continue; + if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) + dac = spec->out_path[i].path[0]; + if (!i && parse_output_path(codec, nid, dac, 1, + &spec->out_mix_path)) + dac = spec->out_mix_path.path[0]; + if (dac) { + spec->private_dac_nids[i] = dac; + dac_num++; } } + if (!spec->out_path[0].depth && spec->out_mix_path.depth) { + spec->out_path[0] = spec->out_mix_path; + spec->out_mix_path.depth = 0; + } + spec->multiout.num_dacs = dac_num; + return 0; +} + +static int create_ch_ctls(struct hda_codec *codec, const char *pfx, + int chs, bool check_dac, struct nid_path *path) +{ + struct via_spec *spec = codec->spec; + char name[32]; + hda_nid_t dac, pin, sel, nid; + int err; + + dac = check_dac ? path->path[0] : 0; + pin = path->path[path->depth - 1]; + sel = path->depth > 1 ? path->path[1] : 0; + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Volume", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->vol_ctl = nid; + } + + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Switch", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->mute_ctl = nid; + } return 0; } +static void mangle_smart51(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg_item *ins = cfg->inputs; + int i, j, nums, attr; + int pins[AUTO_CFG_MAX_INS]; + + for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + nums = 0; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int def; + if (ins[i].type > AUTO_PIN_LINE_IN) + continue; + def = snd_hda_codec_get_pincfg(codec, ins[i].pin); + if (snd_hda_get_input_pin_attr(def) != attr) + continue; + for (j = 0; j < nums; j++) + if (ins[pins[j]].type < ins[i].type) { + memmove(pins + j + 1, pins + j, + (nums - j) * sizeof(int)); + break; + } + pins[j] = i; + nums++; + } + if (cfg->line_outs + nums < 3) + continue; + for (i = 0; i < nums; i++) { + hda_nid_t pin = ins[pins[i]].pin; + spec->smart51_pins[spec->smart51_nums++] = pin; + cfg->line_out_pins[cfg->line_outs++] = pin; + if (cfg->line_outs == 3) + break; + } + return; + } +} + +static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) +{ + dst->vol_ctl = src->vol_ctl; + dst->mute_ctl = src->mute_ctl; +} + /* add playback controls from the parsed DAC table */ -static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static int via_auto_create_multi_out_ctls(struct hda_codec *codec) { - char name[32]; + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct nid_path *path; static const char * const chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; + int i, idx, err; + int old_line_outs; - nid_vol = nid_vols[i]; + /* check smart51 */ + old_line_outs = cfg->line_outs; + if (cfg->line_outs == 1) + mangle_smart51(codec); - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; + err = via_auto_fill_dac_nids(codec); + if (err < 0) + return err; - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); + if (spec->multiout.num_dacs < 3) { + spec->smart51_nums = 0; + cfg->line_outs = old_line_outs; + } + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t pin, dac; + pin = cfg->line_out_pins[i]; + dac = spec->multiout.dac_nids[i]; + if (!pin || !dac) + continue; + path = spec->out_path + i; + if (i == HDA_CLFE) { + err = create_ch_ctls(codec, "Center", 1, true, path); if (err < 0) return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); + err = create_ch_ctls(codec, "LFE", 2, true, path); if (err < 0) return err; } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); + const char *pfx = chname[i]; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs == 1) + pfx = "Speaker"; + err = create_ch_ctls(codec, pfx, 3, true, path); if (err < 0) return err; } + if (path != spec->out_path + i) + copy_path_mixer_ctls(&spec->out_path[i], path); + if (path == spec->out_path && spec->out_mix_path.depth) + copy_path_mixer_ctls(&spec->out_mix_path, path); + } + + idx = get_connection_index(codec, spec->aa_mix_nid, + spec->multiout.dac_nids[0]); + if (idx >= 0) { + /* add control to mixer */ + const char *name; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Volume" : "PCM Playback Volume"; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Switch" : "PCM Playback Switch"; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; } + cfg->line_outs = old_line_outs; + return 0; } -static void create_hp_imux(struct via_spec *spec) +static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { - int i; - struct hda_input_mux *imux = &spec->private_imux[1]; - static const char * const texts[] = { "OFF", "ON", NULL}; + struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; + int i, err; - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); + if (!pin) + return 0; - spec->hp_mux = &spec->private_imux[1]; + if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { + for (i = HDA_SIDE; i >= HDA_CLFE; i--) { + if (i < spec->multiout.num_dacs && + parse_output_path(codec, pin, + spec->multiout.dac_nids[i], 0, + &spec->hp_indep_path)) { + spec->hp_indep_shared = i; + break; + } + } + } + if (spec->hp_indep_path.depth) { + spec->hp_dac_nid = spec->hp_indep_path.path[0]; + if (!spec->hp_indep_shared) + spec->hp_path = spec->hp_indep_path; + } + /* optionally check front-path w/o AA-mix */ + if (!spec->hp_path.depth) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->hp_path); + + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->hp_mix_path) && !spec->hp_path.depth) + return 0; + + if (spec->hp_path.depth) { + path = &spec->hp_path; + check_dac = true; + } else { + path = &spec->hp_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) + copy_path_mixer_ctls(&spec->hp_mix_path, path); + else + copy_path_mixer_ctls(&spec->hp_path, path); + if (spec->hp_indep_path.depth) + copy_path_mixer_ctls(&spec->hp_indep_path, path); + return 0; } -static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +static int via_auto_create_speaker_ctls(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; + hda_nid_t pin, dac; int err; - if (!pin) + pin = spec->autocfg.speaker_pins[0]; + if (!spec->autocfg.speaker_outs || !pin) + return 0; + + if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) + dac = spec->speaker_path.path[0]; + if (!dac) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->speaker_path); + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->speaker_mix_path) && !dac) return 0; - spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; + /* no AA-path for front? */ + if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) + dac = 0; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + spec->speaker_dac_nid = dac; + spec->multiout.extra_out_nid[0] = dac; + if (dac) { + path = &spec->speaker_path; + check_dac = true; + } else { + path = &spec->speaker_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); if (err < 0) return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (check_dac) + copy_path_mixer_ctls(&spec->speaker_mix_path, path); + else + copy_path_mixer_ctls(&spec->speaker_path, path); + return 0; +} + +#define via_aamix_ctl_info via_pin_power_ctl_info + +static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, int do_mix, + struct nid_path *nomix, struct nid_path *mix) +{ + if (do_mix) { + activate_output_path(codec, nomix, false, false); + activate_output_path(codec, mix, true, false); + } else { + activate_output_path(codec, mix, false, false); + activate_output_path(codec, nomix, true, false); + } +} + +static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + /* update front path */ + update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); + /* update HP path */ + if (!spec->hp_independent_mode) { + update_aamix_paths(codec, val, &spec->hp_path, + &spec->hp_mix_path); + } + /* update speaker path */ + update_aamix_paths(codec, val, &spec->speaker_path, + &spec->speaker_mix_path); + return 1; +} + +static const struct snd_kcontrol_new via_aamix_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = via_aamix_ctl_info, + .get = via_aamix_ctl_get, + .put = via_aamix_ctl_put, +}; + +static int via_auto_create_loopback_switch(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->aa_mix_nid || !spec->out_mix_path.depth) + return 0; /* no loopback switching available */ + if (!via_clone_control(spec, &via_aamix_ctl_enum)) + return -ENOMEM; + return 0; +} + +/* look for ADCs */ +static int via_fill_adcs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid = codec->start_nid; + int i; + + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (wcaps & AC_WCAP_DIGITAL) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) + return -ENOMEM; + spec->adc_nids[spec->num_adc_nids++] = nid; + } + return 0; +} + +/* input-src control */ +static int via_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + strcpy(uinfo->value.enumerated.name, + spec->inputs[uinfo->value.enumerated.item].label); + return 0; +} + +static int via_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; + return 0; +} + +static int via_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + hda_nid_t mux; + int cur; + + cur = ucontrol->value.enumerated.item[0]; + if (cur < 0 || cur >= spec->num_inputs) + return -EINVAL; + if (spec->cur_mux[idx] == cur) + return 0; + spec->cur_mux[idx] = cur; + if (spec->dyn_adc_switch) { + int adc_idx = spec->inputs[cur].adc_idx; + mux = spec->mux_nids[adc_idx]; + via_dyn_adc_pcm_resetup(codec, cur); + } else { + mux = spec->mux_nids[idx]; + if (snd_BUG_ON(!mux)) + return -EINVAL; + } + + if (mux) { + /* switch to D0 beofre change index */ + if (snd_hda_codec_read(codec, mux, 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, + spec->inputs[cur].mux_idx); + } + + /* update jack power state */ + set_widgets_power_state(codec); + return 0; +} + +static const struct snd_kcontrol_new via_input_src_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, +}; + +static int create_input_src_ctls(struct hda_codec *codec, int count) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (spec->num_inputs <= 1 || !count) + return 0; /* no need for single src */ + + knew = via_clone_control(spec, &via_input_src_ctl); + if (!knew) + return -ENOMEM; + knew->count = count; + return 0; +} + +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct via_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; +} + +static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, + hda_nid_t dst) +{ + return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; +} + +/* add the input-route to the given pin */ +static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int c, idx; + + spec->inputs[spec->num_inputs].adc_idx = -1; + spec->inputs[spec->num_inputs].pin = pin; + for (c = 0; c < spec->num_adc_nids; c++) { + if (spec->mux_nids[c]) { + idx = get_connection_index(codec, spec->mux_nids[c], + pin); + if (idx < 0) + continue; + spec->inputs[spec->num_inputs].mux_idx = idx; + } else { + if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) + continue; + } + spec->inputs[spec->num_inputs].adc_idx = c; + /* Can primary ADC satisfy all inputs? */ + if (!spec->dyn_adc_switch && + spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { + snd_printd(KERN_INFO + "via: dynamic ADC switching enabled\n"); + spec->dyn_adc_switch = 1; + } + return true; + } + return false; +} + +static int get_mux_nids(struct hda_codec *codec); + +/* parse input-routes; fill ADCs, MUXs and input-src entries */ +static int parse_analog_inputs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + err = via_fill_adcs(codec); + if (err < 0) + return err; + err = get_mux_nids(codec); if (err < 0) return err; - create_hp_imux(spec); + /* fill all input-routes */ + for (i = 0; i < cfg->num_inputs; i++) { + if (add_input_route(codec, cfg->inputs[i].pin)) + spec->inputs[spec->num_inputs++].label = + hda_get_autocfg_input_label(codec, cfg, i); + } + + /* check for internal loopback recording */ + if (spec->aa_mix_nid && + add_input_route(codec, spec->aa_mix_nid)) + spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; return 0; } -/* create playback/capture controls for input pins */ -static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - hda_nid_t cap_nid, - const hda_nid_t pin_idxs[], - int num_idxs) +/* create analog-loopback volume/switch controls */ +static int create_loopback_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, type, type_idx = 0; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, j, err, idx; - /* for internal loopback recording select */ - for (idx = 0; idx < num_idxs; idx++) { - if (pin_idxs[idx] == 0xff) { - snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); - break; + if (!spec->aa_mix_nid) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *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; + idx = get_connection_index(codec, spec->aa_mix_nid, pin); + if (idx >= 0) { + err = via_new_analog_input(spec, label, type_idx, + idx, spec->aa_mix_nid); + if (err < 0) + return err; + add_loopback_list(spec, spec->aa_mix_nid, idx); + } + + /* remember the label for smart51 control */ + for (j = 0; j < spec->smart51_nums; j++) { + if (spec->smart51_pins[j] == pin) { + spec->smart51_idxs[j] = idx; + spec->smart51_labels[j] = label; + break; + } } } + return 0; +} + +/* create mic-boost controls (if present) */ +static int create_mic_boost_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int caps; const char *label; - type = cfg->inputs[i].type; - for (idx = 0; idx < num_idxs; idx++) - if (pin_idxs[idx] == cfg->inputs[i].pin) - break; - if (idx >= num_idxs) + char name[32]; + + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + caps = query_amp_caps(codec, pin, HDA_INPUT); + if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) continue; - if (i > 0 && type == cfg->inputs[i - 1].type) - type_idx++; - else - type_idx = 0; label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->codec_type == VT1708S || - spec->codec_type == VT1702 || - spec->codec_type == VT1716S) - err = via_new_analog_input(spec, label, type_idx, - idx+1, cap_nid); - else - err = via_new_analog_input(spec, label, type_idx, - idx, cap_nid); + snprintf(name, sizeof(name), "%s Boost Volume", label); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; +} + +/* create capture and input-src controls for multiple streams */ +static int create_multi_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i, err; + + /* create capture mixer elements */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t adc = spec->adc_nids[i]; + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Capture Volume", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Capture Switch", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); if (err < 0) return err; - snd_hda_add_imux_item(imux, label, idx, NULL); } + + /* input-source control */ + for (i = 0; i < spec->num_adc_nids; i++) + if (!spec->mux_nids[i]) + break; + err = create_input_src_ctls(codec, i); + if (err < 0) + return err; return 0; } -/* create playback/capture controls for input pins */ -static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +/* bind capture volume/switch */ +static struct snd_kcontrol_new via_bind_cap_vol_ctl = + HDA_BIND_VOL("Capture Volume", 0); +static struct snd_kcontrol_new via_bind_cap_sw_ctl = + HDA_BIND_SW("Capture Switch", 0); + +static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, + struct hda_ctl_ops *ops) { - static const hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, - ARRAY_SIZE(pin_idxs)); + struct hda_bind_ctls *ctl; + int i; + + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->ops = ops; + for (i = 0; i < spec->num_adc_nids; i++) + ctl->values[i] = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); + *ctl_ret = ctl; + return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708_loopbacks[] = { - { 0x17, HDA_INPUT, 1 }, - { 0x17, HDA_INPUT, 2 }, - { 0x17, HDA_INPUT, 3 }, - { 0x17, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif +/* create capture and input-src controls for dynamic ADC-switch case */ +static int create_dyn_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + int err; + + /* set up the bind capture ctls */ + err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); + if (err < 0) + return err; + err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); + if (err < 0) + return err; + + /* create capture mixer elements */ + knew = via_clone_control(spec, &via_bind_cap_vol_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_vol; + + knew = via_clone_control(spec, &via_bind_cap_sw_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_sw; + + /* input-source control */ + err = create_input_src_ctls(codec, 1); + if (err < 0) + return err; + return 0; +} + +/* parse and create capture-related stuff */ +static int via_auto_create_analog_input_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = parse_analog_inputs(codec); + if (err < 0) + return err; + if (spec->dyn_adc_switch) + err = create_dyn_adc_ctls(codec); + else + err = create_multi_adc_ctls(codec); + if (err < 0) + return err; + err = create_loopback_ctls(codec); + if (err < 0) + return err; + err = create_mic_boost_ctls(codec); + if (err < 0) + return err; + return 0; +} static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { @@ -2095,7 +2591,7 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) return; } -static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2103,13 +2599,13 @@ static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = + spec->vt1708_jack_detect = !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); - ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; return 0; } -static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2118,98 +2614,150 @@ static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; + spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) - == !spec->vt1708_jack_detectect; - if (spec->vt1708_jack_detectect) { + == !spec->vt1708_jack_detect; + if (spec->vt1708_jack_detect) { mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } return change; } -static const struct snd_kcontrol_new vt1708_jack_detectect[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Jack Detect", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = vt1708_jack_detectect_get, - .put = vt1708_jack_detectect_put, - }, - {} /* end */ +static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detect_get, + .put = vt1708_jack_detect_put, }; -static int vt1708_parse_auto_config(struct hda_codec *codec) +static void fill_dig_outs(struct hda_codec *codec); +static void fill_dig_in(struct hda_codec *codec); + +static int via_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int err; - /* Add HP and CD pin config connect bit re-config action */ - vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); - vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + return -EINVAL; - err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = via_auto_create_multi_out_ctls(codec); if (err < 0) return err; - err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); + err = via_auto_create_speaker_ctls(codec); if (err < 0) return err; - /* add jack detect on/off control */ - err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); + err = via_auto_create_loopback_switch(codec); + if (err < 0) + return err; + err = via_auto_create_analog_input_ctls(codec); if (err < 0) return err; spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; - spec->dig_in_pin = VT1708_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708_DIGIN_NID; + fill_dig_outs(codec); + fill_dig_in(codec); if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; - spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; - spec->input_mux = &spec->private_imux[0]; + if (spec->hp_dac_nid && spec->hp_mix_path.depth) { + err = via_hp_build(codec); + if (err < 0) + return err; + } - if (spec->hp_mux) - via_hp_build(codec); + err = via_smart51_build(codec); + if (err < 0) + return err; + + /* assign slave outs */ + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; - via_smart51_build(spec); return 1; } -/* init callback for auto-configuration model -- overriding the default init */ -static int via_auto_init(struct hda_codec *codec) +static void via_auto_init_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (spec->multiout.dig_out_nid) + init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); + if (spec->slave_dig_outs[0]) + init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); +} + +static void via_auto_init_dig_in(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + if (!spec->dig_in_nid) + return; + snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); +} + +/* initialize the unsolicited events */ +static void via_auto_init_unsol_event(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int ev; + int i; + + if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) + snd_hda_codec_write(codec, cfg->hp_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); + + if (cfg->speaker_pins[0]) + ev = VIA_LINE_EVENT; + else + ev = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (cfg->line_out_pins[i] && + is_jack_detectable(codec, cfg->line_out_pins[i])) + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ev | VIA_JACK_EVENT); + } + + for (i = 0; i < cfg->num_inputs; i++) { + if (is_jack_detectable(codec, cfg->inputs[i].pin)) + snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT); + } +} + +static int via_init(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_iverbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); - via_init(codec); via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); + via_auto_init_speaker_out(codec); via_auto_init_analog_input(codec); + via_auto_init_dig_outs(codec); + via_auto_init_dig_in(codec); - if (VT2002P_COMPATIBLE(spec)) { - via_hp_bind_automute(codec); - } else { - via_hp_automute(codec); - via_speaker_automute(codec); - } + via_auto_init_unsol_event(codec); + + via_hp_automute(codec); return 0; } @@ -2266,437 +2814,36 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x17; + + /* Add HP and CD pin config connect bit re-config action */ + vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); + vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); + /* automatic parse from the BIOS config */ - err = vt1708_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } + /* add jack detect on/off control */ + if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) + return -ENOMEM; - spec->stream_name_analog = "VT1708 Analog"; - spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ if (codec->vendor_id == 0x11061708) spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; - spec->stream_analog_capture = &vt1708_pcm_analog_capture; - - spec->stream_name_digital = "VT1708 Digital"; - spec->stream_digital_playback = &vt1708_pcm_digital_playback; - spec->stream_digital_capture = &vt1708_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708_loopbacks; -#endif INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1709_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - { } -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, 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 - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } -}; - -static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 10, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x14, /* NID to query formats and rates */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close - }, -}; - -static const struct hda_pcm_stream vt1709_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int vt1709_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - if (cfg->line_outs == 4) /* 10 channels */ - spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ - else if (cfg->line_outs == 3) /* 6 channels */ - spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ - - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_outs == 4) { /* 10 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW3 */ - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - /* AOW1 */ - spec->private_dac_nids[i] = 0x27; - break; - default: - break; - } - } - } - spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ - - } else if (cfg->line_outs == 3) { /* 6 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->private_dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW1 */ - spec->private_dac_nids[i] = 0x11; - break; - default: - break; - } - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* ADD control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SURROUND) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SIDE) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - if (spec->multiout.num_dacs == 5) /* 10 channels */ - spec->multiout.hp_nid = VT1709_HP_DAC_NID; - else if (spec->multiout.num_dacs == 3) /* 6 channels */ - spec->multiout.hp_nid = 0; - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1709_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; - spec->dig_in_pin = VT1709_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1709_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1709_loopbacks[] = { - { 0x18, HDA_INPUT, 1 }, - { 0x18, HDA_INPUT, 2 }, - { 0x18, HDA_INPUT, 3 }, - { 0x18, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - -static int patch_vt1709_10ch(struct hda_codec *codec) +static int patch_vt1709(struct hda_codec *codec) { struct via_spec *spec; int err; @@ -2706,528 +2853,19 @@ static int patch_vt1709_10ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - err = vt1709_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - - spec->stream_name_analog = "VT1709 Analog"; - spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_name_digital = "VT1709 Digital"; - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; - } - - codec->patch_ops = via_patch_ops; - - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif - - return 0; -} -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, 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 - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } -}; - -static int patch_vt1709_6ch(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + spec->aa_mix_nid = 0x18; - err = vt1709_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - - spec->stream_name_analog = "VT1709 Analog"; - spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_name_digital = "VT1709 Digital"; - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; } codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif - return 0; -} - -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, 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 - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - -static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, 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 - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - -static const struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static int via_pcm_open_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - - analog_low_current_mode(codec, idle); - return 0; -} - -static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 4, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x25; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; - hda_nid_t nid, nid_vol = 0; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; } -/* create playback/capture controls for input pins */ -static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1708B_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; - spec->dig_in_pin = VT1708B_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708B_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708B_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1708B(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -3309,157 +2947,37 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) } static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B_8ch(struct hda_codec *codec) +static int patch_vt1708B(struct hda_codec *codec) { struct via_spec *spec; int err; if (get_codec_type(codec) == VT1708BCE) return patch_vt1708S(codec); - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - - spec->stream_name_analog = "VT1708B Analog"; - spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_name_digital = "VT1708B Digital"; - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; - } - - codec->patch_ops = via_patch_ops; - - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - - return 0; -} - -static int patch_vt1708B_4ch(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; /* create a codec specific record */ spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - - spec->stream_name_analog = "VT1708B Analog"; - spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_name_digital = "VT1708B Digital"; - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; } codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } /* Patch for VT1708S */ - -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt1708S_volume_init_verbs[] = { - /* Unmute ADC0-1 and set the default input to mic-in */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, 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 */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9, PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, /* don't bybass mixer */ @@ -3467,277 +2985,6 @@ static const struct hda_verb vt1708S_volume_init_verbs[] = { { } }; -static const struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_verb vt1705_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_pcm_stream vt1708S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1705_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - if (spec->codec->vendor_id == 0x11064397) - spec->private_dac_nids[i] = 0x25; - else - spec->private_dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0x25; - break; - } - } - } - - /* for Smart 5.1, line/mic inputs double as output pins */ - if (cfg->line_outs == 1) { - spec->multiout.num_dacs = 3; - spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11; - if (spec->codec->vendor_id == 0x11064397) - spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25; - else - spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct via_spec *spec = codec->spec; - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, - {0x10, 0x11, 0x25, 0} }; - hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, - {0x1C, 0x18, 0x27, 0} }; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - /* for Smart 5.1, there are always at least six channels */ - if (!nid && i > AUTO_SEQ_CENLFE) - continue; - - if (codec->vendor_id == 0x11064397) { - nid_vol = nid_vols[1][i]; - nid_mute = nid_mutes[1][i]; - } else { - nid_vol = nid_vols[0][i]; - nid_mute = nid_mutes[0][i]; - } - if (!nid_vol && !nid_mute) - continue; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - /* fill out digital output widgets; one for master and one for slave outputs */ static void fill_dig_outs(struct hda_codec *codec) { @@ -3763,56 +3010,33 @@ static void fill_dig_outs(struct hda_codec *codec) } } -static int vt1708S_parse_auto_config(struct hda_codec *codec) +static void fill_dig_in(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; + hda_nid_t dig_nid; + int i, err; - if (spec->hp_mux) - via_hp_build(codec); + if (!spec->autocfg.dig_in_pin) + return; - via_smart51_build(spec); - return 1; + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1708S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { @@ -3833,62 +3057,21 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + /* automatic parse from the BIOS config */ - err = vt1708S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; - if (codec->vendor_id == 0x11064397) - spec->init_verbs[spec->num_iverbs++] = - vt1705_uniwill_init_verbs; - else - spec->init_verbs[spec->num_iverbs++] = - vt1708S_uniwill_init_verbs; - - if (codec->vendor_id == 0x11060440) - spec->stream_name_analog = "VT1818S Analog"; - else if (codec->vendor_id == 0x11064397) - spec->stream_name_analog = "VT1705 Analog"; - else - spec->stream_name_analog = "VT1708S Analog"; - if (codec->vendor_id == 0x11064397) - spec->stream_analog_playback = &vt1705_pcm_analog_playback; - else - spec->stream_analog_playback = &vt1708S_pcm_analog_playback; - spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - - if (codec->vendor_id == 0x11060440) - spec->stream_name_digital = "VT1818S Digital"; - else if (codec->vendor_id == 0x11064397) - spec->stream_name_digital = "VT1705 Digital"; - else - spec->stream_name_digital = "VT1708S Digital"; - spec->stream_digital_playback = &vt1708S_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708S_loopbacks; -#endif - /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); @@ -3896,13 +3079,6 @@ static int patch_vt1708S(struct hda_codec *codec) snprintf(codec->bus->card->mixername, sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); - spec->stream_name_analog = "VT1708BCE Analog"; - spec->stream_name_digital = "VT1708BCE Digital"; - } - /* correct names for VT1818S */ - if (codec->vendor_id == 0x11060440) { - spec->stream_name_analog = "VT1818S Analog"; - spec->stream_name_digital = "VT1818S Digital"; } /* correct names for VT1705 */ if (codec->vendor_id == 0x11064397) { @@ -3918,55 +3094,7 @@ static int patch_vt1708S(struct hda_codec *codec) /* Patch for VT1702 */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1702_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt1702_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x20, 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 - */ - /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* Setup default input of PW4 to MW0 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* PW6 PW7 Output enable */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1702_init_verbs[] = { /* mixer enable */ {0x1, 0xF88, 0x3}, /* GPIO 0~2 */ @@ -3974,202 +3102,6 @@ static const struct hda_verb vt1702_volume_init_verbs[] = { { } }; -static const struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_pcm_stream vt1702_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1702_pcm_analog_capture = { - .substreams = 3, - .channels_min = 2, - .channels_max = 2, - .nid = 0x12, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static const struct hda_pcm_stream vt1702_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1702_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_out_pins[0]) { - /* config dac list */ - spec->private_dac_nids[0] = 0x10; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - - if (!cfg->line_out_pins[0]) - return -1; - - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err, i; - struct hda_input_mux *imux; - static const char * const texts[] = { "ON", "OFF", NULL}; - if (!pin) - return 0; - spec->multiout.hp_nid = 0x1D; - spec->hp_independent_mode_index = 0; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - imux = &spec->private_imux[1]; - - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); - - spec->hp_mux = &spec->private_imux[1]; - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1702_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - /* limit AA path volume to 0 dB */ - snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1702_loopbacks[] = { - { 0x1A, HDA_INPUT, 1 }, - { 0x1A, HDA_INPUT, 2 }, - { 0x1A, HDA_INPUT, 3 }, - { 0x1A, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1702(struct hda_codec *codec) { int imux_is_smixer = @@ -4211,393 +3143,41 @@ static int patch_vt1702(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x1a; + + /* limit AA path volume to 0 dB */ + snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + /* automatic parse from the BIOS config */ - err = vt1702_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; - - spec->stream_name_analog = "VT1702 Analog"; - spec->stream_analog_playback = &vt1702_pcm_analog_playback; - spec->stream_analog_capture = &vt1702_pcm_analog_capture; - - spec->stream_name_digital = "VT1702 Digital"; - spec->stream_digital_playback = &vt1702_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1702_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1702_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1702_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1702; return 0; } /* Patch for VT1718S */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt1718S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - +static const struct hda_verb vt1718S_init_verbs[] = { /* Enable MW0 adjust Gain 5 */ {0x1, 0xfb2, 0x10}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - /* PW9 PW10 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* PW11 Input enable */ - {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, /* Enable Boost Volume backdoor */ {0x1, 0xf88, 0x8}, - /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, - { } -}; - -static const struct hda_verb vt1718S_uniwill_init_verbs[] = { - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; -static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 10, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x8; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0xa; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x9; - break; - case AUTO_SEQ_SIDE: - spec->private_dac_nids[i] = 0xb; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; - hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; - hda_nid_t nid, nid_vol, nid_mute = 0; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x21, 3, 5, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x21, 3, 5, - HDA_INPUT)); - if (err < 0) - return err; - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0xc; /* AOW4 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1718S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - - if (err < 0) - return err; - err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) - spec->dig_in_nid = 0x13; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1718S_loopbacks[] = { - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { 0x21, HDA_INPUT, 3 }, - { 0x21, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1718S(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -4664,6 +3244,41 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) } } +/* Add a connection to the primary DAC from AA-mixer for some codecs + * This isn't listed from the raw info, but the chip has a secret connection. + */ +static int add_secret_dac_path(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i, nums; + hda_nid_t conn[8]; + hda_nid_t nid; + + if (!spec->aa_mix_nid) + return 0; + nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn) - 1); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) + return 0; + } + + /* find the primary DAC and add to the connection list */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int caps = get_wcaps(codec, nid); + if (get_wcaps_type(caps) == AC_WID_AUD_OUT && + !(caps & AC_WCAP_DIGITAL)) { + conn[nums++] = nid; + return snd_hda_override_conn_list(codec, + spec->aa_mix_nid, + nums, conn); + } + } + return 0; +} + + static int patch_vt1718S(struct hda_codec *codec) { struct via_spec *spec; @@ -4674,57 +3289,22 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); + /* automatic parse from the BIOS config */ - err = vt1718S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - - if (codec->vendor_id == 0x11060441) - spec->stream_name_analog = "VT2020 Analog"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_analog = "VT1828S Analog"; - else - spec->stream_name_analog = "VT1718S Analog"; - spec->stream_analog_playback = &vt1718S_pcm_analog_playback; - spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - - if (codec->vendor_id == 0x11060441) - spec->stream_name_digital = "VT2020 Digital"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_digital = "VT1828S Digital"; - else - spec->stream_name_digital = "VT1718S Digital"; - spec->stream_digital_playback = &vt1718S_pcm_digital_playback; - if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) - spec->stream_digital_capture = &vt1718S_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1718S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1718S_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1718S; return 0; @@ -4770,26 +3350,6 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, return 1; } -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), { @@ -4811,45 +3371,7 @@ static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { { } /* end */ }; -static const struct hda_verb vt1716S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Stereo Mixer = 5 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - - /* Setup default input of SW1 as MW0 */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* Setup default input of SW4 as AOW0 */ - {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* PW9 PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - - /* Unmute SW1, PW12 */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* PW12 Output enable */ - {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1716S_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xf8a, 0x80}, /* don't bybass mixer */ @@ -4859,272 +3381,6 @@ static const struct hda_verb vt1716S_volume_init_verbs[] = { { } }; - -static const struct hda_verb vt1716S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_pcm_stream vt1716S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1716S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 3; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->private_dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->private_dac_nids[i] = 0x25; - break; - case AUTO_SEQ_SURROUND: - spec->private_dac_nids[i] = 0x11; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[3] = { - "Front", "Surround", "C/LFE" - }; - hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; - hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x25; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1716S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1716S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1716S(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5228,35 +3484,18 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + /* automatic parse from the BIOS config */ - err = vt1716S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; - - spec->stream_name_analog = "VT1716S Analog"; - spec->stream_analog_playback = &vt1716S_pcm_analog_playback; - spec->stream_analog_capture = &vt1716S_pcm_analog_capture; - - spec->stream_name_digital = "VT1716S Digital"; - spec->stream_digital_playback = &vt1716S_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1716S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; spec->num_mixers++; @@ -5265,354 +3504,32 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1716S_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1716S; return 0; } /* for vt2002P */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt2002P_volume_init_verbs[] = { +static const struct hda_verb vt2002P_init_verbs[] = { /* Class-D speaker related verbs */ {0x1, 0xfe0, 0x4}, {0x1, 0xfe9, 0x80}, {0x1, 0xfe2, 0x22}, - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/8 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x37, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, - - /* set PW0 index=0 (MW0) */ - {0x24, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } }; -static const struct hda_verb vt1802_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, +static const struct hda_verb vt1802_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/8 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x38, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, - - /* set PW0 index=0 (MW0) */ - {0x24, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } }; - -static const struct hda_verb vt2002P_uniwill_init_verbs[] = { - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; -static const struct hda_verb vt1802_uniwill_init_verbs[] = { - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_pcm_stream vt2002P_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt2002P_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->private_dac_nids[0] = 0x8; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - hda_nid_t sw_nid; - - if (!cfg->line_out_pins[0]) - return -1; - - if (spec->codec_type == VT1802) - sw_nid = 0x28; - else - sw_nid = 0x26; - - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); - if (err < 0) - return err; - - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); - - return 0; -} - -static int vt2002P_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt2002P_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt2002P(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -5735,334 +3652,39 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); + /* automatic parse from the BIOS config */ - err = vt2002P_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = - vt1802_volume_init_verbs; - else - spec->init_verbs[spec->num_iverbs++] = - vt2002P_volume_init_verbs; - - if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = - vt1802_uniwill_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; else - spec->init_verbs[spec->num_iverbs++] = - vt2002P_uniwill_init_verbs; - - if (spec->codec_type == VT1802) - spec->stream_name_analog = "VT1802 Analog"; - else - spec->stream_name_analog = "VT2002P Analog"; - spec->stream_analog_playback = &vt2002P_pcm_analog_playback; - spec->stream_analog_capture = &vt2002P_pcm_analog_capture; - - if (spec->codec_type == VT1802) - spec->stream_name_digital = "VT1802 Digital"; - else - spec->stream_name_digital = "VT2002P Digital"; - spec->stream_digital_playback = &vt2002P_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt2002P_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt2002P_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt2002P; return 0; } /* for vt1812 */ -/* capture mixer elements */ -static const struct snd_kcontrol_new vt1812_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_verb vt1812_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - +static const struct hda_verb vt1812_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/13/15 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x38, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0xa8}, { } }; - -static const struct hda_verb vt1812_uniwill_init_verbs[] = { - {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static const struct hda_pcm_stream vt1812_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1812_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static const struct hda_pcm_stream vt1812_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1812_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->private_dac_nids[0] = 0x8; - return 0; -} - - -/* add playback controls from the parsed DAC table */ -static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; - - if (!cfg->line_out_pins[0]) - return -1; - - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; - - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); - if (err < 0) - return err; - - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); - - return 0; -} - -static int vt1812_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - fill_dig_outs(codec); - err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) - return 0; /* can't find valid BIOS pin config */ - - err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list vt1812_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - static void set_widgets_power_state_vt1812(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -6166,47 +3788,22 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->aa_mix_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); + /* automatic parse from the BIOS config */ - err = vt1812_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - - spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; - - spec->stream_name_analog = "VT1812 Analog"; - spec->stream_analog_playback = &vt1812_pcm_analog_playback; - spec->stream_analog_capture = &vt1812_pcm_analog_capture; - - spec->stream_name_digital = "VT1812 Digital"; - spec->stream_digital_playback = &vt1812_pcm_digital_playback; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1812_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1812_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1812_loopbacks; -#endif - spec->set_widgets_power_state = set_widgets_power_state_vt1812; return 0; } @@ -6220,37 +3817,37 @@ static const struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106e710, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e711, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e712, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e713, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e714, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e715, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e716, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e717, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e720, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e721, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e722, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e723, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e724, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e725, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e726, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e727, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x11060397, .name = "VT1708S", .patch = patch_vt1708S}, { .id = 0x11061397, .name = "VT1708S", diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index f4594d76b6ea..0ccc0eb75775 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -87,7 +87,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ static char *model[SNDRV_CARDS]; static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */ -static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */ +static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transceiver reset timeout value in msec */ static int dxr_enable[SNDRV_CARDS]; /* DXR enable for DMX6FIRE */ module_param_array(index, int, NULL, 0444); @@ -2607,7 +2607,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card, ice->profi_port = pci_resource_start(pci, 3); if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_SHARED, - "ICE1712", ice)) { + KBUILD_MODNAME, ice)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ice1712_free(ice); return -EIO; @@ -2802,7 +2802,7 @@ static void __devexit snd_ice1712_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "ICE1712", + .name = KBUILD_MODNAME, .id_table = snd_ice1712_ids, .probe = snd_ice1712_probe, .remove = __devexit_p(snd_ice1712_remove), diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index c1498fa5545f..c2b7f8bc41e4 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2509,7 +2509,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card, ice->profi_port = pci_resource_start(pci, 1); if (request_irq(pci->irq, snd_vt1724_interrupt, - IRQF_SHARED, "ICE1724", ice)) { + IRQF_SHARED, KBUILD_MODNAME, ice)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_vt1724_free(ice); return -EIO; @@ -2802,7 +2802,7 @@ static int snd_vt1724_resume(struct pci_dev *pci) #endif static struct pci_driver driver = { - .name = "ICE1724", + .name = KBUILD_MODNAME, .id_table = snd_vt1724_ids, .probe = snd_vt1724_probe, .remove = __devexit_p(snd_vt1724_remove), diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6c896dbfd796..6a5b387b97fd 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1884,6 +1884,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { }, { .subvendor = 0x1028, + .subdevice = 0x0189, + .name = "Dell Inspiron 9300", + .type = AC97_TUNE_HP_MUTE_LED + }, + { + .subvendor = 0x1028, .subdevice = 0x0191, .name = "Dell Inspiron 8600", .type = AC97_TUNE_HP_ONLY @@ -2647,7 +2653,7 @@ static int intel8x0_resume(struct pci_dev *pci) pci_set_master(pci); snd_intel8x0_chip_init(chip, 0); if (request_irq(pci->irq, snd_intel8x0_interrupt, - IRQF_SHARED, card->shortname, chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { printk(KERN_ERR "intel8x0: unable to grab IRQ %d, " "disabling device\n", pci->irq); snd_card_disconnect(card); @@ -3106,7 +3112,7 @@ static int __devinit snd_intel8x0_create(struct snd_card *card, /* request irq after initializaing int_sta_mask, etc */ if (request_irq(pci->irq, snd_intel8x0_interrupt, - IRQF_SHARED, card->shortname, chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_intel8x0_free(chip); return -EBUSY; @@ -3266,7 +3272,7 @@ static void __devexit snd_intel8x0_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Intel ICH", + .name = KBUILD_MODNAME, .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .remove = __devexit_p(snd_intel8x0_remove), diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index f3353b49c785..7c161645d865 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1047,7 +1047,7 @@ static int intel8x0m_resume(struct pci_dev *pci) } pci_set_master(pci); if (request_irq(pci->irq, snd_intel8x0m_interrupt, - IRQF_SHARED, card->shortname, chip)) { + IRQF_SHARED, KBUILD_MODNAME, chip)) { printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, " "disabling device\n", pci->irq); snd_card_disconnect(card); @@ -1174,7 +1174,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, port_inited: if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, - card->shortname, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_intel8x0m_free(chip); return -EBUSY; @@ -1325,7 +1325,7 @@ static void __devexit snd_intel8x0m_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Intel ICH Modem", + .name = KBUILD_MODNAME, .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .remove = __devexit_p(snd_intel8x0m_remove), diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 6d795700be79..fc1d573cf306 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2241,7 +2241,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev * err = request_irq(pci->irq, snd_korg1212_interrupt, IRQF_SHARED, - "korg1212", korg1212); + KBUILD_MODNAME, korg1212); if (err) { snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq); @@ -2477,7 +2477,7 @@ static void __devexit snd_korg1212_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "korg1212", + .name = KBUILD_MODNAME, .id_table = snd_korg1212_ids, .probe = snd_korg1212_probe, .remove = __devexit_p(snd_korg1212_remove), diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index 2692e5ae5f2d..3e92e5b5ec3d 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -648,7 +648,7 @@ static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci, goto errout; if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED, - DRVNAME, chip)) { + KBUILD_MODNAME, chip)) { printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto errout; @@ -771,7 +771,7 @@ MODULE_DEVICE_TABLE(pci, lola_ids); /* pci_driver definition */ static struct pci_driver driver = { - .name = DRVNAME, + .name = KBUILD_MODNAME, .id_table = lola_ids, .probe = lola_probe, .remove = __devexit_p(lola_remove), diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h index d5708e29b16d..f0b100059efd 100644 --- a/sound/pci/lola/lola.h +++ b/sound/pci/lola/lola.h @@ -480,7 +480,7 @@ struct lola { /* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */ #define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f) -#define LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(res) ((res >> 7) & 0x1f) +#define LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(res) ((res >> 7) & 0x1f) int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb, unsigned int data, unsigned int extdata); diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index 5d518f1a712c..6b8d64812951 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -144,40 +144,61 @@ int __devinit lola_init_mixer_widget(struct lola *chip, int nid) chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams; chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins; - /* mixer matrix can have unused areas between PhysIn and + /* mixer matrix may have unused areas between PhysIn and * Play or Record and PhysOut zones */ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins + LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val); chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins + - LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val); - - /* example : MixerMatrix of LoLa881 - * 0-------8------16-------8------16 - * | | | | | - * | INPUT | | INPUT | | - * | -> |unused | -> |unused | - * | RECORD| | OUTPUT| | - * | | | | | - * 8-------------------------------- - * | | | | | - * | | | | | - * |unused |unused |unused |unused | - * | | | | | - * | | | | | - * 16------------------------------- - * | | | | | - * | PLAY | | PLAY | | - * | -> |unused | -> |unused | - * | RECORD| | OUTPUT| | - * | | | | | - * 8-------------------------------- - * | | | | | - * | | | | | - * |unused |unused |unused |unused | - * | | | | | - * | | | | | - * 16------------------------------- + LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(val); + + /* example : MixerMatrix of LoLa881 (LoLa16161 uses unused zones) + * +-+ 0-------8------16-------8------16 + * | | | | | | | + * |s| | INPUT | | INPUT | | + * | |->| -> |unused | -> |unused | + * |r| |CAPTURE| | OUTPUT| | + * | | | MIX | | MIX | | + * |c| 8-------------------------------- + * | | | | | | | + * | | | | | | | + * |g| |unused |unused |unused |unused | + * | | | | | | | + * |a| | | | | | + * | | 16------------------------------- + * |i| | | | | | + * | | | PLAYBK| | PLAYBK| | + * |n|->| -> |unused | -> |unused | + * | | |CAPTURE| | OUTPUT| | + * | | | MIX | | MIX | | + * |a| 8-------------------------------- + * |r| | | | | | + * |r| | | | | | + * |a| |unused |unused |unused |unused | + * |y| | | | | | + * | | | | | | | + * +++ 16--|---------------|------------ + * +---V---------------V-----------+ + * | dest_mix_gain_enable array | + * +-------------------------------+ + */ + /* example : MixerMatrix of LoLa280 + * +-+ 0-------8-2 + * | | | | | + * |s| | INPUT | | INPUT + * |r|->| -> | | -> + * |c| |CAPTURE| | <- OUTPUT + * | | | MIX | | MIX + * |g| 8---------- + * |a| | | | + * |i| | PLAYBK| | PLAYBACK + * |n|->| -> | | -> + * | | |CAPTURE| | <- OUTPUT + * |a| | MIX | | MIX + * |r| 8---|----|- + * |r| +---V----V-------------------+ + * |a| | dest_mix_gain_enable array | + * |y| +----------------------------+ */ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT || chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) { @@ -192,6 +213,9 @@ int __devinit lola_init_mixer_widget(struct lola *chip, int nid) (((1U << chip->mixer.dest_phys_outs) - 1) << chip->mixer.dest_phys_out_ofs); + snd_printdd("Mixer src_mask=%x, dest_mask=%x\n", + chip->mixer.src_mask, chip->mixer.dest_mask); + return 0; } @@ -202,12 +226,19 @@ static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id, if (!(chip->mixer.src_mask & (1 << id))) return -EINVAL; - writew(gain, &chip->mixer.array->src_gain[id]); oldval = val = readl(&chip->mixer.array->src_gain_enable); if (on) val |= (1 << id); else val &= ~(1 << id); + /* test if values unchanged */ + if ((val == oldval) && + (gain == readw(&chip->mixer.array->src_gain[id]))) + return 0; + + snd_printdd("lola_mixer_set_src_gain (id=%d, gain=%d) enable=%x\n", + id, gain, val); + writew(gain, &chip->mixer.array->src_gain[id]); writel(val, &chip->mixer.array->src_gain_enable); lola_codec_flush(chip); /* inform micro-controller about the new source gain */ @@ -269,6 +300,7 @@ static int lola_mixer_set_mapping_gain(struct lola *chip, src, dest); } +#if 0 /* not used */ static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id, unsigned int mask, unsigned short *gains) { @@ -289,6 +321,7 @@ static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id, return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_DESTINATION_GAIN, id, 0); } +#endif /* not used */ /* */ @@ -376,6 +409,8 @@ static int set_analog_volume(struct lola *chip, int dir, return 0; if (external_call) lola_codec_flush(chip); + snd_printdd("set_analog_volume (dir=%d idx=%d, volume=%d)\n", + dir, idx, val); err = lola_codec_write(chip, pin->nid, LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0); if (err < 0) @@ -427,23 +462,40 @@ static int init_mixer_values(struct lola *chip) { int i; - /* all src on */ + /* all sample rate converters on */ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false); - /* clear all matrix */ + /* clear all mixer matrix settings */ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array)); - /* set src gain to 0dB */ + /* inform firmware about all updated matrix columns - capture part */ + for (i = 0; i < chip->mixer.dest_stream_ins; i++) + lola_codec_write(chip, chip->mixer.nid, + LOLA_VERB_SET_DESTINATION_GAIN, + i, 0); + /* inform firmware about all updated matrix columns - output part */ + for (i = 0; i < chip->mixer.dest_phys_outs; i++) + lola_codec_write(chip, chip->mixer.nid, + LOLA_VERB_SET_DESTINATION_GAIN, + chip->mixer.dest_phys_out_ofs + i, 0); + + /* set all digital input source (master) gains to 0dB */ for (i = 0; i < chip->mixer.src_phys_ins; i++) lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */ + + /* set all digital playback source (master) gains to 0dB */ for (i = 0; i < chip->mixer.src_stream_outs; i++) lola_mixer_set_src_gain(chip, i + chip->mixer.src_stream_out_ofs, 336, true); /* 0dB */ - /* set 1:1 dest gain */ + /* set gain value 0dB diagonally in matrix - part INPUT -> CAPTURE */ for (i = 0; i < chip->mixer.dest_stream_ins; i++) { int src = i % chip->mixer.src_phys_ins; lola_mixer_set_mapping_gain(chip, src, i, 336, true); } + /* set gain value 0dB diagonally in matrix , part PLAYBACK -> OUTPUT + * (LoLa280 : playback channel 0,2,4,6 linked to output channel 0) + * (LoLa280 : playback channel 1,3,5,7 linked to output channel 1) + */ for (i = 0; i < chip->mixer.src_stream_outs; i++) { int src = chip->mixer.src_stream_out_ofs + i; int dst = chip->mixer.dest_phys_out_ofs + @@ -693,6 +745,7 @@ static int __devinit create_src_gain_mixer(struct lola *chip, snd_ctl_new1(&lola_src_gain_mixer, chip)); } +#if 0 /* not used */ /* * destination gain (matrix-like) mixer */ @@ -781,6 +834,7 @@ static int __devinit create_dest_gain_mixer(struct lola *chip, return snd_ctl_add(chip->card, snd_ctl_new1(&lola_dest_gain_mixer, chip)); } +#endif /* not used */ /* */ @@ -798,14 +852,16 @@ int __devinit lola_create_mixer(struct lola *chip) if (err < 0) return err; err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0, - "Line Source Gain Volume"); + "Digital Capture Volume"); if (err < 0) return err; err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs, chip->mixer.src_stream_out_ofs, - "Stream Source Gain Volume"); + "Digital Playback Volume"); if (err < 0) return err; +#if 0 +/* FIXME: buggy mixer matrix handling */ err = create_dest_gain_mixer(chip, chip->mixer.src_phys_ins, 0, chip->mixer.dest_stream_ins, 0, @@ -834,6 +890,6 @@ int __devinit lola_create_mixer(struct lola *chip) "Stream Playback Volume"); if (err < 0) return err; - +#endif /* FIXME */ return init_mixer_values(chip); } diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 1bd7a540fd49..04ae84b2a107 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -762,7 +762,6 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran) static int __devinit lx_init_dsp(struct lx6464es *chip) { int err; - u8 mac_address[6]; int i; snd_printdd("->lx_init_dsp\n"); @@ -787,11 +786,11 @@ static int __devinit lx_init_dsp(struct lx6464es *chip) /** \todo the mac address should be ready by not, but it isn't, * so we wait for it */ for (i = 0; i != 1000; ++i) { - err = lx_dsp_get_mac(chip, mac_address); + err = lx_dsp_get_mac(chip); if (err) return err; - if (mac_address[0] || mac_address[1] || mac_address[2] || - mac_address[3] || mac_address[4] || mac_address[5]) + if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] || + chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5]) goto mac_ready; msleep(1); } @@ -800,8 +799,8 @@ static int __devinit lx_init_dsp(struct lx6464es *chip) mac_ready: snd_printd(LXP "mac address ready read after: %dms\n", i); snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n", - mac_address[0], mac_address[1], mac_address[2], - mac_address[3], mac_address[4], mac_address[5]); + chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], + chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); err = lx_init_get_version_features(chip); if (err) @@ -1031,7 +1030,7 @@ static int __devinit snd_lx6464es_create(struct snd_card *card, chip->port_dsp_bar = pci_ioremap_bar(pci, 2); err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED, - card_name, chip); + KBUILD_MODNAME, chip); if (err) { snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq); goto request_irq_failed; @@ -1108,8 +1107,14 @@ static int __devinit snd_lx6464es_probe(struct pci_dev *pci, goto out_free; } - strcpy(card->driver, "lx6464es"); - strcpy(card->shortname, "Digigram LX6464ES"); + strcpy(card->driver, "LX6464ES"); + sprintf(card->id, "LX6464ES_%02X%02X%02X", + chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); + + sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X", + chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], + chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); + sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i", card->shortname, chip->port_plx, chip->port_dsp_bar, chip->irq); @@ -1137,7 +1142,7 @@ static void __devexit snd_lx6464es_remove(struct pci_dev *pci) static struct pci_driver driver = { - .name = "Digigram LX6464ES", + .name = KBUILD_MODNAME, .id_table = snd_lx6464es_ids, .probe = snd_lx6464es_probe, .remove = __devexit_p(snd_lx6464es_remove), diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h index aea621eafbb5..6792eda9c9a5 100644 --- a/sound/pci/lx6464es/lx6464es.h +++ b/sound/pci/lx6464es/lx6464es.h @@ -26,7 +26,7 @@ #define LX6464ES_H #include <linux/spinlock.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <sound/core.h> #include <sound/pcm.h> @@ -69,6 +69,8 @@ struct lx6464es { struct pci_dev *pci; int irq; + u8 mac_address[6]; + spinlock_t lock; /* interrupt spinlock */ struct mutex setup_mutex; /* mutex used in hw_params, open * and close */ diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 617f98b0cbae..5c8717e29eeb 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -424,7 +424,7 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq) return ret; } -int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address) +int lx_dsp_get_mac(struct lx6464es *chip) { u32 macmsb, maclsb; @@ -432,12 +432,12 @@ int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address) maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF; /* todo: endianess handling */ - mac_address[5] = ((u8 *)(&maclsb))[0]; - mac_address[4] = ((u8 *)(&maclsb))[1]; - mac_address[3] = ((u8 *)(&maclsb))[2]; - mac_address[2] = ((u8 *)(&macmsb))[0]; - mac_address[1] = ((u8 *)(&macmsb))[1]; - mac_address[0] = ((u8 *)(&macmsb))[2]; + chip->mac_address[5] = ((u8 *)(&maclsb))[0]; + chip->mac_address[4] = ((u8 *)(&maclsb))[1]; + chip->mac_address[3] = ((u8 *)(&maclsb))[2]; + chip->mac_address[2] = ((u8 *)(&macmsb))[0]; + chip->mac_address[1] = ((u8 *)(&macmsb))[1]; + chip->mac_address[0] = ((u8 *)(&macmsb))[2]; return 0; } diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h index 6bd9cbbbc68d..1dd562980b6c 100644 --- a/sound/pci/lx6464es/lx_core.h +++ b/sound/pci/lx6464es/lx_core.h @@ -116,7 +116,7 @@ int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version); int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq); int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran); int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data); -int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address); +int lx_dsp_get_mac(struct lx6464es *chip); /* low-level pipe handling */ diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 3c40d726b46e..0378126e6272 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -850,11 +850,10 @@ struct snd_m3 { struct input_dev *input_dev; char phys[64]; /* physical device path */ #else - spinlock_t ac97_lock; struct snd_kcontrol *master_switch; struct snd_kcontrol *master_volume; - struct tasklet_struct hwvol_tq; #endif + struct work_struct hwvol_work; unsigned int in_suspend; @@ -1609,13 +1608,10 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s) (without wrap around) in response to volume button presses and then generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 of a byte wide register. The meaning of bits 0 and 4 is unknown. */ -static void snd_m3_update_hw_volume(unsigned long private_data) +static void snd_m3_update_hw_volume(struct work_struct *work) { - struct snd_m3 *chip = (struct snd_m3 *) private_data; + struct snd_m3 *chip = container_of(work, struct snd_m3, hwvol_work); int x, val; -#ifndef CONFIG_SND_MAESTRO3_INPUT - unsigned long flags; -#endif /* Figure out which volume control button was pushed, based on differences from the default register @@ -1645,21 +1641,13 @@ static void snd_m3_update_hw_volume(unsigned long private_data) if (!chip->master_switch || !chip->master_volume) return; - /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ - spin_lock_irqsave(&chip->ac97_lock, flags); - - val = chip->ac97->regs[AC97_MASTER_VOL]; + val = snd_ac97_read(chip->ac97, AC97_MASTER); switch (x) { case 0x88: /* The counters have not changed, yet we've received a HV interrupt. According to tests run by various people this happens when pressing the mute button. */ val ^= 0x8000; - chip->ac97->regs[AC97_MASTER_VOL] = val; - outw(val, chip->iobase + CODEC_DATA); - outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_switch->id); break; case 0xaa: /* counters increased by 1 -> volume up */ @@ -1667,11 +1655,6 @@ static void snd_m3_update_hw_volume(unsigned long private_data) val--; if ((val & 0x7f00) > 0) val -= 0x0100; - chip->ac97->regs[AC97_MASTER_VOL] = val; - outw(val, chip->iobase + CODEC_DATA); - outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_volume->id); break; case 0x66: /* counters decreased by 1 -> volume down */ @@ -1679,14 +1662,11 @@ static void snd_m3_update_hw_volume(unsigned long private_data) val++; if ((val & 0x7f00) < 0x1f00) val += 0x0100; - chip->ac97->regs[AC97_MASTER_VOL] = val; - outw(val, chip->iobase + CODEC_DATA); - outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->master_volume->id); break; } - spin_unlock_irqrestore(&chip->ac97_lock, flags); + if (snd_ac97_update(chip->ac97, AC97_MASTER, val)) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_switch->id); #else if (!chip->input_dev) return; @@ -1730,11 +1710,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) return IRQ_NONE; if (status & HV_INT_PENDING) -#ifdef CONFIG_SND_MAESTRO3_INPUT - snd_m3_update_hw_volume((unsigned long)chip); -#else - tasklet_schedule(&chip->hwvol_tq); -#endif + schedule_work(&chip->hwvol_work); /* * ack an assp int if its running @@ -2000,24 +1976,14 @@ static unsigned short snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { struct snd_m3 *chip = ac97->private_data; -#ifndef CONFIG_SND_MAESTRO3_INPUT - unsigned long flags; -#endif unsigned short data = 0xffff; if (snd_m3_ac97_wait(chip)) goto fail; -#ifndef CONFIG_SND_MAESTRO3_INPUT - spin_lock_irqsave(&chip->ac97_lock, flags); -#endif snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); if (snd_m3_ac97_wait(chip)) - goto fail_unlock; + goto fail; data = snd_m3_inw(chip, CODEC_DATA); -fail_unlock: -#ifndef CONFIG_SND_MAESTRO3_INPUT - spin_unlock_irqrestore(&chip->ac97_lock, flags); -#endif fail: return data; } @@ -2026,20 +1992,11 @@ static void snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct snd_m3 *chip = ac97->private_data; -#ifndef CONFIG_SND_MAESTRO3_INPUT - unsigned long flags; -#endif if (snd_m3_ac97_wait(chip)) return; -#ifndef CONFIG_SND_MAESTRO3_INPUT - spin_lock_irqsave(&chip->ac97_lock, flags); -#endif snd_m3_outw(chip, val, CODEC_DATA); snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); -#ifndef CONFIG_SND_MAESTRO3_INPUT - spin_unlock_irqrestore(&chip->ac97_lock, flags); -#endif } @@ -2458,6 +2415,7 @@ static int snd_m3_free(struct snd_m3 *chip) struct m3_dma *s; int i; + cancel_work_sync(&chip->hwvol_work); #ifdef CONFIG_SND_MAESTRO3_INPUT if (chip->input_dev) input_unregister_device(chip->input_dev); @@ -2511,6 +2469,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) return 0; chip->in_suspend = 1; + cancel_work_sync(&chip->hwvol_work); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); @@ -2667,9 +2626,6 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, } spin_lock_init(&chip->reg_lock); -#ifndef CONFIG_SND_MAESTRO3_INPUT - spin_lock_init(&chip->ac97_lock); -#endif switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: @@ -2683,6 +2639,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, chip->card = card; chip->pci = pci; chip->irq = -1; + INIT_WORK(&chip->hwvol_work, snd_m3_update_hw_volume); chip->external_amp = enable_amp; if (amp_gpio >= 0 && amp_gpio <= 0x0f) @@ -2752,12 +2709,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, snd_m3_hv_init(chip); -#ifndef CONFIG_SND_MAESTRO3_INPUT - tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); -#endif - if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED, - card->driver, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_m3_free(chip); return -ENOMEM; @@ -2885,7 +2838,7 @@ static void __devexit snd_m3_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Maestro3", + .name = KBUILD_MODNAME, .id_table = snd_m3_ids, .probe = snd_m3_probe, .remove = __devexit_p(snd_m3_remove), diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 6c3fd4d1c49d..dbee59906ae1 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1268,7 +1268,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci, } if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED, - CARD_NAME, mgr)) { + KBUILD_MODNAME, mgr)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_mixart_free(mgr); return -EBUSY; @@ -1381,7 +1381,7 @@ static void __devexit snd_mixart_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Digigram miXart", + .name = KBUILD_MODNAME, .id_table = snd_mixart_ids, .probe = snd_mixart_probe, .remove = __devexit_p(snd_mixart_remove), diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 5a60492ac7b3..83ea7a7d3eec 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -465,7 +465,7 @@ static int snd_nm256_acquire_irq(struct nm256 *chip) mutex_lock(&chip->irq_mutex); if (chip->irq < 0) { if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED, - chip->card->driver, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq); mutex_unlock(&chip->irq_mutex); return -EBUSY; @@ -1743,7 +1743,7 @@ static void __devexit snd_nm256_remove(struct pci_dev *pci) static struct pci_driver driver = { - .name = "NeoMagic 256", + .name = KBUILD_MODNAME, .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .remove = __devexit_p(snd_nm256_remove), diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index d7e8ddd9a67b..218d9854e5cb 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -859,7 +859,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci, } static struct pci_driver oxygen_driver = { - .name = "CMI8788", + .name = KBUILD_MODNAME, .id_table = oxygen_ids, .probe = generic_oxygen_probe, .remove = __devexit_p(oxygen_pci_remove), diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 70b739816fcc..82311fcb86f6 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -655,7 +655,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, chip->model.init(chip); err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED, - DRIVER, chip); + KBUILD_MODNAME, chip); if (err < 0) { snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq); goto err_card; diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index d5533e34ece9..cc0bcd9f3350 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -168,12 +168,6 @@ static int oxygen_open(struct snd_pcm_substream *substream, if (err < 0) return err; } - if (channel == PCM_MULTICH) { - err = snd_pcm_hw_constraint_minmax - (runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000); - if (err < 0) - return err; - } snd_pcm_set_sync(substream); chip->streams[channel] = substream; diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 469010a8b849..773db794b43f 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -88,7 +88,7 @@ static int __devinit xonar_probe(struct pci_dev *pci, } static struct pci_driver xonar_driver = { - .name = "AV200", + .name = KBUILD_MODNAME, .id_table = xonar_ids, .probe = xonar_probe, .remove = __devexit_p(oxygen_pci_remove), diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 54cad38ec30a..32d096c98f5b 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -327,8 +327,10 @@ static void pcm1796_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; - data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD; + if (!data->broken_i2c) + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE; data->pcm1796_regs[0][19 - PCM1796_REG_BASE] = PCM1796_FLT_SHARP | PCM1796_ATS_1; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = @@ -1123,6 +1125,7 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model.control_filter = xonar_st_h6_control_filter; chip->model.dac_channels_pcm = 8; chip->model.dac_channels_mixer = 8; + chip->model.dac_volume_min = 255; chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128); break; } diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 95cfde27d25c..046578d26f98 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1501,7 +1501,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, mgr->irq = -1; if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED, - card_name, mgr)) { + KBUILD_MODNAME, mgr)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); pcxhr_free(mgr); return -EBUSY; @@ -1608,7 +1608,7 @@ static void __devexit pcxhr_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Digigram pcxhr", + .name = KBUILD_MODNAME, .id_table = pcxhr_ids, .probe = pcxhr_probe, .remove = __devexit_p(pcxhr_remove), diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index ad5202efd7a9..e34ae14908b3 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1890,7 +1890,7 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci, UNSET_AIE(hwport); if (request_irq(pci->irq, snd_riptide_interrupt, IRQF_SHARED, - "RIPTIDE", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n", pci->irq); snd_riptide_free(chip); @@ -2176,7 +2176,7 @@ static void __devexit snd_card_riptide_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RIPTIDE", + .name = KBUILD_MODNAME, .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .remove = __devexit_p(snd_card_riptide_remove), @@ -2188,7 +2188,7 @@ static struct pci_driver driver = { #ifdef SUPPORT_JOYSTICK static struct pci_driver joystick_driver = { - .name = "Riptide Joystick", + .name = KBUILD_MODNAME "-joystick", .id_table = snd_riptide_joystick_ids, .probe = snd_riptide_joystick_probe, .remove = __devexit_p(snd_riptide_joystick_remove), diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 3c04524de37c..6be77a264d47 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1355,7 +1355,7 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) } if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_SHARED, - "RME32", rme32)) { + KBUILD_MODNAME, rme32)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); return -EBUSY; } @@ -1985,7 +1985,7 @@ static void __devexit snd_rme32_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RME Digi32", + .name = KBUILD_MODNAME, .id_table = snd_rme32_ids, .probe = snd_rme32_probe, .remove = __devexit_p(snd_rme32_remove), diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 9ff247fc8871..409e5b89519d 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1561,7 +1561,7 @@ snd_rme96_create(struct rme96 *rme96) } if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_SHARED, - "RME96", rme96)) { + KBUILD_MODNAME, rme96)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); return -EBUSY; } @@ -2396,7 +2396,7 @@ static void __devexit snd_rme96_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RME Digi96", + .name = KBUILD_MODNAME, .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .remove = __devexit_p(snd_rme96_remove), diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 2d8332416c83..1c6d1e1c27c1 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5482,7 +5482,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card, } if (request_irq(pci->irq, snd_hdsp_interrupt, IRQF_SHARED, - "hdsp", hdsp)) { + KBUILD_MODNAME, hdsp)) { snd_printk(KERN_ERR "Hammerfall-DSP: unable to use IRQ %d\n", pci->irq); return -EBUSY; } @@ -5637,7 +5637,7 @@ static void __devexit snd_hdsp_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RME Hammerfall DSP", + .name = KBUILD_MODNAME, .id_table = snd_hdsp_ids, .probe = snd_hdsp_probe, .remove = __devexit_p(snd_hdsp_remove), diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index c8e402fc3782..af130ee0c45d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6441,7 +6441,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card, hdspm->port + io_extent - 1); if (request_irq(pci->irq, snd_hdspm_interrupt, - IRQF_SHARED, "hdspm", hdspm)) { + IRQF_SHARED, KBUILD_MODNAME, hdspm)) { snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); return -EBUSY; } @@ -6779,7 +6779,7 @@ static void __devexit snd_hdspm_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RME Hammerfall DSP MADI", + .name = KBUILD_MODNAME, .id_table = snd_hdspm_ids, .probe = snd_hdspm_probe, .remove = __devexit_p(snd_hdspm_remove), diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index c492af5b25f3..1c7bc1ef8186 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2479,7 +2479,7 @@ static int __devinit snd_rme9652_create(struct snd_card *card, } if (request_irq(pci->irq, snd_rme9652_interrupt, IRQF_SHARED, - "rme9652", rme9652)) { + KBUILD_MODNAME, rme9652)) { snd_printk(KERN_ERR "unable to request IRQ %d\n", pci->irq); return -EBUSY; } @@ -2632,7 +2632,7 @@ static void __devexit snd_rme9652_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "RME Digi9652 (Hammerfall)", + .name = KBUILD_MODNAME, .id_table = snd_rme9652_ids, .probe = snd_rme9652_probe, .remove = __devexit_p(snd_rme9652_remove), diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 2b5c7a95ae1f..bcf61524a13f 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1235,7 +1235,7 @@ static int sis_resume(struct pci_dev *pci) } if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, - card->shortname, sis)) { + KBUILD_MODNAME, sis)) { printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq); goto error; } @@ -1341,7 +1341,7 @@ static int __devinit sis_chip_create(struct snd_card *card, goto error_out_cleanup; if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, - card->shortname, sis)) { + KBUILD_MODNAME, sis)) { printk(KERN_ERR "unable to allocate irq %d\n", sis->irq); goto error_out_cleanup; } @@ -1436,7 +1436,7 @@ static void __devexit snd_sis7019_remove(struct pci_dev *pci) } static struct pci_driver sis7019_driver = { - .name = "SiS7019", + .name = KBUILD_MODNAME, .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .remove = __devexit_p(snd_sis7019_remove), diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 337b9facadfd..2571a67b389a 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1294,7 +1294,7 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card, sonic->game_port = pci_resource_start(pci, 4); if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_SHARED, - "S3 SonicVibes", sonic)) { + KBUILD_MODNAME, sonic)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_sonicvibes_free(sonic); return -EBUSY; @@ -1530,7 +1530,7 @@ static void __devexit snd_sonic_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "S3 SonicVibes", + .name = KBUILD_MODNAME, .id_table = snd_sonic_ids, .probe = snd_sonic_probe, .remove = __devexit_p(snd_sonic_remove), diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 6d0581841d7a..d8a128f6fc02 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -172,7 +172,7 @@ static void __devexit snd_trident_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Trident4DWaveAudio", + .name = KBUILD_MODNAME, .id_table = snd_trident_ids, .probe = snd_trident_probe, .remove = __devexit_p(snd_trident_remove), diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 2870a4fdc130..5bd57a7c52d2 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3598,7 +3598,7 @@ int __devinit snd_trident_create(struct snd_card *card, trident->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED, - "Trident Audio", trident)) { + KBUILD_MODNAME, trident)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_trident_free(trident); return -EBUSY; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8c5f8b5a59f0..f03fd620a2a0 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2377,7 +2377,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card, chip_type == TYPE_VIA8233 ? snd_via8233_interrupt : snd_via686_interrupt, IRQF_SHARED, - card->driver, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_via82xx_free(chip); return -EBUSY; @@ -2611,7 +2611,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "VIA 82xx Audio", + .name = KBUILD_MODNAME, .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index f7e8bbbe3953..a386dd9f6732 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1129,7 +1129,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card, } chip->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_via82xx_interrupt, IRQF_SHARED, - card->driver, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_via82xx_free(chip); return -EBUSY; @@ -1224,7 +1224,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "VIA 82xx Modem", + .name = KBUILD_MODNAME, .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 99a9a814be0b..5342d5e1366a 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -169,7 +169,7 @@ static int __devinit snd_vx222_create(struct snd_card *card, struct pci_dev *pci vx->port[i] = pci_resource_start(pci, i + 1); if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED, - CARD_NAME, chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_vx222_free(chip); return -EBUSY; @@ -290,7 +290,7 @@ static int snd_vx222_resume(struct pci_dev *pci) #endif static struct pci_driver driver = { - .name = "Digigram VX222", + .name = KBUILD_MODNAME, .id_table = snd_vx222_ids, .probe = snd_vx222_probe, .remove = __devexit_p(snd_vx222_remove), diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 80c682113381..511d57653124 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -345,7 +345,7 @@ static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci) } static struct pci_driver driver = { - .name = "Yamaha DS-1 PCI", + .name = KBUILD_MODNAME, .id_table = snd_ymfpci_ids, .probe = snd_card_ymfpci_probe, .remove = __devexit_p(snd_card_ymfpci_remove), diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index c94c051ad0c8..f3260e658b8a 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2380,7 +2380,7 @@ int __devinit snd_ymfpci_create(struct snd_card *card, return -EBUSY; } if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED, - "YMFPCI", chip)) { + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ymfpci_free(chip); return -EBUSY; diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index ce33be0e4e98..66488a7a5706 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -223,7 +223,7 @@ static int pdacf_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_exclusive_irq(link, pdacf_interrupt); + ret = pcmcia_request_irq(link, pdacf_interrupt); if (ret) goto failed; diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index d9ef21d8fa73..31777d1ea49f 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -229,7 +229,7 @@ static int vxpocket_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_exclusive_irq(link, snd_vx_irq_handler); + ret = pcmcia_request_irq(link, snd_vx_irq_handler); if (ret) goto failed; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 3ecbd67f88c9..ab96cde7417b 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -881,8 +881,7 @@ static int snd_pmac_free(struct snd_pmac *chip) for (i = 0; i < 3; i++) { if (chip->requested & (1 << i)) release_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1); + resource_size(&chip->rsrc[i])); } } @@ -1228,8 +1227,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) goto __error; } if (request_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1, + resource_size(&chip->rsrc[i]), rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " " %d (%s: %pR)\n", @@ -1254,8 +1252,7 @@ int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) goto __error; } if (request_mem_region(chip->rsrc[i].start, - chip->rsrc[i].end - - chip->rsrc[i].start + 1, + resource_size(&chip->rsrc[i]), rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " " %d (%s: %pR)\n", diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 1ed61c5df2c5..4f913876f332 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,5 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o +snd-soc-core-objs += soc-pcm.o soc-io.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index d0e75323ec19..f81d4c3f8956 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -364,9 +364,11 @@ static struct snd_pcm_ops atmel_pcm_ops = { \*--------------------------------------------------------------------------*/ static u64 atmel_pcm_dmamask = 0xffffffff; -static int atmel_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) @@ -382,7 +384,7 @@ static int atmel_pcm_new(struct snd_card *card, } if (dai->driver->capture.channels_min) { - pr_debug("at32-pcm:" + pr_debug("atmel-pcm:" "Allocating PCM capture DMA buffer\n"); ret = atmel_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 2597329302e7..5e0a95e64329 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -60,7 +60,7 @@ struct atmel_ssc_mask { * This structure, shared between the PCM driver and the interface, * contains all information required by the PCM driver to perform the * PDC DMA operation. All fields except dma_intr_handler() are initialized - * by the interface. The dms_intr_handler() pointer is set by the PCM + * by the interface. The dma_intr_handler() pointer is set by the PCM * driver and called by the interface SSC interrupt handler if it is * non-NULL. */ diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index eda955b15834..71225090c49f 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -402,7 +402,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S && bits > 16) { printk(KERN_WARNING - "atmel_ssc_dai: sample size %d" + "atmel_ssc_dai: sample size %d " "is too large for I2S\n", bits); return -EINVAL; } @@ -838,10 +838,8 @@ int atmel_ssc_set_audio(int ssc_id) } ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); - if (!ssc_pdev) { - ssc_free(ssc); + if (!ssc_pdev) return -ENOMEM; - } /* If we can grab the SSC briefly to parent the DAI device off it */ ssc = ssc_request(ssc_id); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 95572d290c27..bad3aa14d5b3 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -92,6 +92,7 @@ static struct snd_soc_ops at91sam9g20ek_ops = { }; static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { static int mclk_on; diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 10fdd2854e58..20bb53a837b1 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -319,10 +319,11 @@ static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static int au1xpsc_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1); diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index ae403597fd31..fe9d548a6837 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -10,13 +10,36 @@ config SND_BF5XX_I2S config SND_BF5XX_SOC_SSM2602 tristate "SoC SSM2602 Audio support for BF52x ezkit" - depends on SND_BF5XX_I2S + depends on SND_BF5XX_I2S && (SPI_MASTER || I2C) select SND_BF5XX_SOC_I2S select SND_SOC_SSM2602 - select I2C help Say Y if you want to add support for SoC audio on BF527-EZKIT. +config SND_SOC_BFIN_EVAL_ADAU1701 + tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAU1701 + select I2C + help + Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + +config SND_SOC_BFIN_EVAL_ADAV80X + tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards" + depends on SND_BF5XX_I2S && (SPI_MASTER || I2C) + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAV80X + help + Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or + EVAL-ADAV803 board connected to one of the Blackfin evaluation boards + like the BF5XX-STAMP or BF5XX-EZKIT. + + Note: This driver assumes that the ADAV80X digital record and playback + interfaces are connected to the first SPORT port on the BF5XX board. + config SND_BF5XX_SOC_AD73311 tristate "SoC AD73311 Audio support for Blackfin" depends on SND_BF5XX_I2S diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 49af3f32aec8..6018bf52a234 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -21,9 +21,13 @@ snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o snd-ad193x-objs := bf5xx-ad193x.o +snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o +snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 98b44b316e78..9e59f680bc19 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -418,9 +418,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); -int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; pr_debug("%s enter\n", __func__); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index f1fd95bb6416..61ddf942fd4d 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -168,7 +168,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); - ret = snd_pcm_hw_constraint_integer(runtime, \ + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) goto out; @@ -257,9 +257,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); -int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; pr_debug("%s enter\n", __func__); @@ -304,8 +306,8 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev) static struct platform_driver bfin_i2s_pcm_driver = { .driver = { - .name = "bfin-i2s-pcm-audio", - .owner = THIS_MODULE, + .name = "bfin-i2s-pcm-audio", + .owner = THIS_MODULE, }, .probe = bfin_i2s_soc_platform_probe, diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index 07cfc7a9e49a..c95cc03d583d 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -283,9 +283,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); -static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c new file mode 100644 index 000000000000..e5550acba2c2 --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -0,0 +1,139 @@ +/* + * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau1701.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = { + { "Speaker", NULL, "OUT0" }, + { "Speaker", NULL, "OUT1" }, + { "Line Out", NULL, "OUT2" }, + { "Line Out", NULL, "OUT3" }, + + { "IN0", NULL, "Line In" }, + { "IN1", NULL, "Line In" }, +}; + +static int bfin_eval_adau1701_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000, + SND_SOC_CLOCK_IN); + + return ret; +} + +static struct snd_soc_ops bfin_eval_adau1701_ops = { + .hw_params = bfin_eval_adau1701_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = { + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + }, + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + }, +}; + +static struct snd_soc_card bfin_eval_adau1701 = { + .name = "bfin-eval-adau1701", + .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM], + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets), + .dapm_routes = bfin_eval_adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes), +}; + +static int bfin_eval_adau1701_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adau1701; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adau1701); +} + +static int __devexit bfin_eval_adau1701_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver bfin_eval_adau1701_driver = { + .driver = { + .name = "bfin-eval-adau1701", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1701_probe, + .remove = __devexit_p(bfin_eval_adau1701_remove), +}; + +static int __init bfin_eval_adau1701_init(void) +{ + return platform_driver_register(&bfin_eval_adau1701_driver); +} +module_init(bfin_eval_adau1701_init); + +static void __exit bfin_eval_adau1701_exit(void) +{ + platform_driver_unregister(&bfin_eval_adau1701_driver); +} +module_exit(bfin_eval_adau1701_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1701"); diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c new file mode 100644 index 000000000000..8d014d01626e --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adav80x.c @@ -0,0 +1,173 @@ +/* + * Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "../codecs/adav80x.h" + +static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = { + { "Line Out", NULL, "VOUTL" }, + { "Line Out", NULL, "VOUTR" }, + + { "VINL", NULL, "Line In" }, + { "VINR", NULL, "Line In" }, +}; + +static int bfin_eval_adav80x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL, + 27000000, params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1, + params_rate(params) * 256, SND_SOC_CLOCK_IN); + + return ret; +} + +static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0, + SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0, + SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0, + SND_SOC_CLOCK_OUT); + + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0); + + return 0; +} + +static struct snd_soc_ops bfin_eval_adav80x_ops = { + .hw_params = bfin_eval_adav80x_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = { + { + .name = "adav80x", + .stream_name = "ADAV80x HiFi", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adav80x-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .init = bfin_eval_adav80x_codec_init, + .ops = &bfin_eval_adav80x_ops, + }, +}; + +static struct snd_soc_card bfin_eval_adav80x = { + .name = "bfin-eval-adav80x", + .dai_link = bfin_eval_adav80x_dais, + .num_links = ARRAY_SIZE(bfin_eval_adav80x_dais), + + .dapm_widgets = bfin_eval_adav80x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets), + .dapm_routes = bfin_eval_adav80x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes), +}; + +enum bfin_eval_adav80x_type { + BFIN_EVAL_ADAV801, + BFIN_EVAL_ADAV803, +}; + +static int bfin_eval_adav80x_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adav80x; + const char *codec_name; + + switch (platform_get_device_id(pdev)->driver_data) { + case BFIN_EVAL_ADAV801: + codec_name = "spi0.1"; + break; + case BFIN_EVAL_ADAV803: + codec_name = "adav803.0-0034"; + break; + default: + return -EINVAL; + } + + bfin_eval_adav80x_dais[0].codec_name = codec_name; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adav80x); +} + +static int __devexit bfin_eval_adav80x_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct platform_device_id bfin_eval_adav80x_ids[] = { + { "bfin-eval-adav801", BFIN_EVAL_ADAV801 }, + { "bfin-eval-adav803", BFIN_EVAL_ADAV803 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids); + +static struct platform_driver bfin_eval_adav80x_driver = { + .driver = { + .name = "bfin-eval-adav80x", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adav80x_probe, + .remove = __devexit_p(bfin_eval_adav80x_remove), + .id_table = bfin_eval_adav80x_ids, +}; + +static int __init bfin_eval_adav80x_init(void) +{ + return platform_driver_register(&bfin_eval_adav80x_driver); +} +module_init(bfin_eval_adav80x_init); + +static void __exit bfin_eval_adav80x_exit(void) +{ + platform_driver_unregister(&bfin_eval_adav80x_driver); +} +module_exit(bfin_eval_adav80x_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 98175a096df2..379b2e3afd98 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_AD73311 + select SND_SOC_ADAV80X select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C @@ -42,6 +43,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI + select SND_SOC_STA32X if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -71,6 +73,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8770 if SPI_MASTER select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8782 select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C select SND_SOC_WM8903 if I2C @@ -84,6 +87,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8971 if I2C select SND_SOC_WM8974 if I2C select SND_SOC_WM8978 if I2C + select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8990 if I2C @@ -130,7 +134,14 @@ config SND_SOC_AD1980 config SND_SOC_AD73311 tristate - + +config SND_SOC_ADAU1701 + select SIGMA + tristate + +config SND_SOC_ADAV80X + tristate + config SND_SOC_ADS117X tristate @@ -216,6 +227,9 @@ config SND_SOC_SPDIF config SND_SOC_SSM2602 tristate +config SND_SOC_STA32X + tristate + config SND_SOC_STAC9766 tristate @@ -236,10 +250,11 @@ config SND_SOC_TLV320DAC33 tristate config SND_SOC_TWL4030 - select TWL4030_CODEC + select MFD_TWL4030_AUDIO tristate config SND_SOC_TWL6040 + select TWL6040_CORE tristate config SND_SOC_UDA134X @@ -299,6 +314,9 @@ config SND_SOC_WM8770 config SND_SOC_WM8776 tristate +config SND_SOC_WM8782 + tristate + config SND_SOC_WM8804 tristate @@ -338,6 +356,9 @@ config SND_SOC_WM8974 config SND_SOC_WM8978 tristate +config SND_SOC_WM8983 + tristate + config SND_SOC_WM8985 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fd8558406ef0..da9990fb8569 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -4,6 +4,8 @@ snd-soc-ad1836-objs := ad1836.o snd-soc-ad193x-objs := ad193x.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o +snd-soc-adau1701-objs := adau1701.o +snd-soc-adav80x-objs := adav80x.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o @@ -28,6 +30,7 @@ snd-soc-alc5623-objs := alc5623.o snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-objs := spdif_transciever.o snd-soc-ssm2602-objs := ssm2602.o +snd-soc-sta32x-objs := sta32x.o snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o @@ -55,6 +58,7 @@ snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm8770-objs := wm8770.o snd-soc-wm8776-objs := wm8776.o +snd-soc-wm8782-objs := wm8782.o snd-soc-wm8804-objs := wm8804.o snd-soc-wm8900-objs := wm8900.o snd-soc-wm8903-objs := wm8903.o @@ -68,6 +72,7 @@ snd-soc-wm8962-objs := wm8962.o snd-soc-wm8971-objs := wm8971.o snd-soc-wm8974-objs := wm8974.o snd-soc-wm8978-objs := wm8978.o +snd-soc-wm8983-objs := wm8983.o snd-soc-wm8985-objs := wm8985.o snd-soc-wm8988-objs := wm8988.o snd-soc-wm8990-objs := wm8990.o @@ -95,6 +100,8 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o +obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o +obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o @@ -120,6 +127,7 @@ obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o @@ -147,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o +obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o @@ -160,6 +169,7 @@ obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o +obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 754c496412bd..4e5c5726366b 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -1,19 +1,10 @@ -/* - * File: sound/soc/codecs/ad1836.c - * Author: Barry Song <Barry.Song@analog.com> - * - * Created: Aug 04 2009 - * Description: Driver for AD1836 sound chip - * - * Modified: - * Copyright 2009 Analog Devices Inc. + /* + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * Copyright 2009-2011 Analog Devices Inc. * - * 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. + * Licensed under the GPL-2 or later. */ #include <linux/init.h> @@ -30,10 +21,15 @@ #include <linux/spi/spi.h> #include "ad1836.h" +enum ad1836_type { + AD1835, + AD1836, + AD1838, +}; + /* codec private data */ struct ad1836_priv { - enum snd_soc_control_type control_type; - void *control_data; + enum ad1836_type type; }; /* @@ -44,29 +40,60 @@ static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"}; static const struct soc_enum ad1836_deemp_enum = SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp); -static const struct snd_kcontrol_new ad1836_snd_controls[] = { - /* DAC volume control */ - SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL, - AD1836_DAC_R1_VOL, 0, 0x3FF, 0), - SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL, - AD1836_DAC_R2_VOL, 0, 0x3FF, 0), - SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL, - AD1836_DAC_R3_VOL, 0, 0x3FF, 0), - - /* ADC switch control */ - SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE, - AD1836_ADCR1_MUTE, 1, 1), - SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE, - AD1836_ADCR2_MUTE, 1, 1), - - /* DAC switch control */ - SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE, - AD1836_DACR1_MUTE, 1, 1), - SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE, - AD1836_DACR2_MUTE, 1, 1), - SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE, - AD1836_DACR3_MUTE, 1, 1), +#define AD1836_DAC_VOLUME(x) \ + SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \ + AD1836_DAC_R_VOL(x), 0, 0x3FF, 0) + +#define AD1836_DAC_SWITCH(x) \ + SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \ + AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) + +#define AD1836_ADC_SWITCH(x) \ + SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \ + AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) + +static const struct snd_kcontrol_new ad183x_dac_controls[] = { + AD1836_DAC_VOLUME(1), + AD1836_DAC_SWITCH(1), + AD1836_DAC_VOLUME(2), + AD1836_DAC_SWITCH(2), + AD1836_DAC_VOLUME(3), + AD1836_DAC_SWITCH(3), + AD1836_DAC_VOLUME(4), + AD1836_DAC_SWITCH(4), +}; + +static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("DAC1OUT"), + SND_SOC_DAPM_OUTPUT("DAC2OUT"), + SND_SOC_DAPM_OUTPUT("DAC3OUT"), + SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_route ad183x_dac_routes[] = { + { "DAC1OUT", NULL, "DAC" }, + { "DAC2OUT", NULL, "DAC" }, + { "DAC3OUT", NULL, "DAC" }, + { "DAC4OUT", NULL, "DAC" }, +}; + +static const struct snd_kcontrol_new ad183x_adc_controls[] = { + AD1836_ADC_SWITCH(1), + AD1836_ADC_SWITCH(2), + AD1836_ADC_SWITCH(3), +}; + +static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("ADC1IN"), + SND_SOC_DAPM_INPUT("ADC2IN"), +}; + +static const struct snd_soc_dapm_route ad183x_adc_routes[] = { + { "ADC", NULL, "ADC1IN" }, + { "ADC", NULL, "ADC2IN" }, +}; +static const struct snd_kcontrol_new ad183x_controls[] = { /* ADC high-pass filter */ SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1, AD1836_ADC_HIGHPASS_FILTER, 1, 0), @@ -75,27 +102,24 @@ static const struct snd_kcontrol_new ad1836_snd_controls[] = { SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum), }; -static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = { +static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1, AD1836_DAC_POWERDOWN, 1), SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1, AD1836_ADC_POWERDOWN, 1, NULL, 0), - SND_SOC_DAPM_OUTPUT("DAC1OUT"), - SND_SOC_DAPM_OUTPUT("DAC2OUT"), - SND_SOC_DAPM_OUTPUT("DAC3OUT"), - SND_SOC_DAPM_INPUT("ADC1IN"), - SND_SOC_DAPM_INPUT("ADC2IN"), }; -static const struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route ad183x_dapm_routes[] = { { "DAC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC_PWR" }, - { "DAC1OUT", "DAC1 Switch", "DAC" }, - { "DAC2OUT", "DAC2 Switch", "DAC" }, - { "DAC3OUT", "DAC3 Switch", "DAC" }, - { "ADC", "ADC1 Switch", "ADC1IN" }, - { "ADC", "ADC2 Switch", "ADC2IN" }, +}; + +static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0); + +static const struct snd_kcontrol_new ad1836_controls[] = { + SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0, + ad1836_in_tlv), }; /* @@ -165,64 +189,69 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, return 0; } +static struct snd_soc_dai_ops ad1836_dai_ops = { + .hw_params = ad1836_hw_params, + .set_fmt = ad1836_set_dai_fmt, +}; + +#define AD183X_DAI(_name, num_dacs, num_adcs) \ +{ \ + .name = _name "-hifi", \ + .playback = { \ + .stream_name = "Playback", \ + .channels_min = 2, \ + .channels_max = (num_dacs) * 2, \ + .rates = SNDRV_PCM_RATE_48000, \ + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ + }, \ + .capture = { \ + .stream_name = "Capture", \ + .channels_min = 2, \ + .channels_max = (num_adcs) * 2, \ + .rates = SNDRV_PCM_RATE_48000, \ + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ + }, \ + .ops = &ad1836_dai_ops, \ +} + +static struct snd_soc_dai_driver ad183x_dais[] = { + [AD1835] = AD183X_DAI("ad1835", 4, 1), + [AD1836] = AD183X_DAI("ad1836", 3, 2), + [AD1838] = AD183X_DAI("ad1838", 3, 1), +}; + #ifdef CONFIG_PM -static int ad1836_soc_suspend(struct snd_soc_codec *codec, - pm_message_t state) +static int ad1836_suspend(struct snd_soc_codec *codec, pm_message_t state) { /* reset clock control mode */ - u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2); - adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK; - - return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2); + return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, 0); } -static int ad1836_soc_resume(struct snd_soc_codec *codec) +static int ad1836_resume(struct snd_soc_codec *codec) { /* restore clock control mode */ - u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2); - adc_ctrl2 |= AD1836_ADC_AUX; - - return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2); + return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX); } #else -#define ad1836_soc_suspend NULL -#define ad1836_soc_resume NULL +#define ad1836_suspend NULL +#define ad1836_resume NULL #endif -static struct snd_soc_dai_ops ad1836_dai_ops = { - .hw_params = ad1836_hw_params, - .set_fmt = ad1836_set_dai_fmt, -}; - -/* codec DAI instance */ -static struct snd_soc_dai_driver ad1836_dai = { - .name = "ad1836-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 2, - .channels_max = 6, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 2, - .channels_max = 4, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &ad1836_dai_ops, -}; - static int ad1836_probe(struct snd_soc_codec *codec) { struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; + int num_dacs, num_adcs; int ret = 0; + int i; + + num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2; + num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2; - codec->control_data = ad1836->control_data; ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI); if (ret < 0) { dev_err(codec->dev, "failed to set cache I/O: %d\n", @@ -239,21 +268,46 @@ static int ad1836_probe(struct snd_soc_codec *codec) snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100); /* unmute adc channles, adc aux mode */ snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180); - /* left/right diff:PGA/MUX */ - snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A); /* volume */ - snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF); - snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF); - snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF); - snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF); - snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF); - snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF); - - snd_soc_add_controls(codec, ad1836_snd_controls, - ARRAY_SIZE(ad1836_snd_controls)); - snd_soc_dapm_new_controls(dapm, ad1836_dapm_widgets, - ARRAY_SIZE(ad1836_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); + for (i = 1; i <= num_dacs; ++i) { + snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF); + snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF); + } + + if (ad1836->type == AD1836) { + /* left/right diff:PGA/MUX */ + snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A); + ret = snd_soc_add_controls(codec, ad1836_controls, + ARRAY_SIZE(ad1836_controls)); + if (ret) + return ret; + } else { + snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00); + } + + ret = snd_soc_add_controls(codec, ad183x_dac_controls, num_dacs * 2); + if (ret) + return ret; + + ret = snd_soc_add_controls(codec, ad183x_adc_controls, num_adcs); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs); + if (ret) + return ret; return ret; } @@ -262,19 +316,24 @@ static int ad1836_probe(struct snd_soc_codec *codec) static int ad1836_remove(struct snd_soc_codec *codec) { /* reset clock control mode */ - u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2); - adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK; - - return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2); + return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, 0); } static struct snd_soc_codec_driver soc_codec_dev_ad1836 = { - .probe = ad1836_probe, - .remove = ad1836_remove, - .suspend = ad1836_soc_suspend, - .resume = ad1836_soc_resume, + .probe = ad1836_probe, + .remove = ad1836_remove, + .suspend = ad1836_suspend, + .resume = ad1836_resume, .reg_cache_size = AD1836_NUM_REGS, .reg_word_size = sizeof(u16), + + .controls = ad183x_controls, + .num_controls = ARRAY_SIZE(ad183x_controls), + .dapm_widgets = ad183x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets), + .dapm_routes = ad183x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes), }; static int __devinit ad1836_spi_probe(struct spi_device *spi) @@ -286,12 +345,12 @@ static int __devinit ad1836_spi_probe(struct spi_device *spi) if (ad1836 == NULL) return -ENOMEM; + ad1836->type = spi_get_device_id(spi)->driver_data; + spi_set_drvdata(spi, ad1836); - ad1836->control_data = spi; - ad1836->control_type = SND_SOC_SPI; ret = snd_soc_register_codec(&spi->dev, - &soc_codec_dev_ad1836, &ad1836_dai, 1); + &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1); if (ret < 0) kfree(ad1836); return ret; @@ -303,27 +362,29 @@ static int __devexit ad1836_spi_remove(struct spi_device *spi) kfree(spi_get_drvdata(spi)); return 0; } +static const struct spi_device_id ad1836_ids[] = { + { "ad1835", AD1835 }, + { "ad1836", AD1836 }, + { "ad1837", AD1835 }, + { "ad1838", AD1838 }, + { "ad1839", AD1838 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, ad1836_ids); static struct spi_driver ad1836_spi_driver = { .driver = { - .name = "ad1836-codec", + .name = "ad1836", .owner = THIS_MODULE, }, .probe = ad1836_spi_probe, .remove = __devexit_p(ad1836_spi_remove), + .id_table = ad1836_ids, }; static int __init ad1836_init(void) { - int ret; - - ret = spi_register_driver(&ad1836_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n", - ret); - } - - return ret; + return spi_register_driver(&ad1836_spi_driver); } module_init(ad1836_init); diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h index 9d6a3f8f8aaf..444747f0db26 100644 --- a/sound/soc/codecs/ad1836.h +++ b/sound/soc/codecs/ad1836.h @@ -1,19 +1,10 @@ /* - * File: sound/soc/codecs/ad1836.h - * Based on: - * Author: Barry Song <Barry.Song@analog.com> + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A * - * Created: Aug 04, 2009 - * Description: definitions for AD1836 registers + * Copyright 2009-2011 Analog Devices Inc. * - * Modified: - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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. + * Licensed under the GPL-2 or later. */ #ifndef __AD1836_H__ @@ -21,39 +12,30 @@ #define AD1836_DAC_CTRL1 0 #define AD1836_DAC_POWERDOWN 2 -#define AD1836_DAC_SERFMT_MASK 0xE0 +#define AD1836_DAC_SERFMT_MASK 0xE0 #define AD1836_DAC_SERFMT_PCK256 (0x4 << 5) #define AD1836_DAC_SERFMT_PCK128 (0x5 << 5) #define AD1836_DAC_WORD_LEN_MASK 0x18 #define AD1836_DAC_WORD_LEN_OFFSET 3 #define AD1836_DAC_CTRL2 1 -#define AD1836_DACL1_MUTE 0 -#define AD1836_DACR1_MUTE 1 -#define AD1836_DACL2_MUTE 2 -#define AD1836_DACR2_MUTE 3 -#define AD1836_DACL3_MUTE 4 -#define AD1836_DACR3_MUTE 5 -#define AD1836_DAC_L1_VOL 2 -#define AD1836_DAC_R1_VOL 3 -#define AD1836_DAC_L2_VOL 4 -#define AD1836_DAC_R2_VOL 5 -#define AD1836_DAC_L3_VOL 6 -#define AD1836_DAC_R3_VOL 7 +/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit + * for the first ADC/DAC */ +#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2) +#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1) + +#define AD1836_DAC_L_VOL(x) ((x) * 2) +#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2)) #define AD1836_ADC_CTRL1 12 #define AD1836_ADC_POWERDOWN 7 #define AD1836_ADC_HIGHPASS_FILTER 8 #define AD1836_ADC_CTRL2 13 -#define AD1836_ADCL1_MUTE 0 -#define AD1836_ADCR1_MUTE 1 -#define AD1836_ADCL2_MUTE 2 -#define AD1836_ADCR2_MUTE 3 #define AD1836_ADC_WORD_LEN_MASK 0x30 #define AD1836_ADC_WORD_OFFSET 5 -#define AD1836_ADC_SERFMT_MASK (7 << 6) +#define AD1836_ADC_SERFMT_MASK (7 << 6) #define AD1836_ADC_SERFMT_PCK256 (0x4 << 6) #define AD1836_ADC_SERFMT_PCK128 (0x5 << 6) #define AD1836_ADC_AUX (0x6 << 6) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c new file mode 100644 index 000000000000..2758d5fc60d6 --- /dev/null +++ b/sound/soc/codecs/adau1701.c @@ -0,0 +1,549 @@ +/* + * Driver for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * based on an inital version by Cliff Cai <cliff.cai@analog.com> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/sigma.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "adau1701.h" + +#define ADAU1701_DSPCTRL 0x1c +#define ADAU1701_SEROCTL 0x1e +#define ADAU1701_SERICTL 0x1f + +#define ADAU1701_AUXNPOW 0x22 + +#define ADAU1701_OSCIPOW 0x26 +#define ADAU1701_DACSET 0x27 + +#define ADAU1701_NUM_REGS 0x28 + +#define ADAU1701_DSPCTRL_CR (1 << 2) +#define ADAU1701_DSPCTRL_DAM (1 << 3) +#define ADAU1701_DSPCTRL_ADM (1 << 4) +#define ADAU1701_DSPCTRL_SR_48 0x00 +#define ADAU1701_DSPCTRL_SR_96 0x01 +#define ADAU1701_DSPCTRL_SR_192 0x02 +#define ADAU1701_DSPCTRL_SR_MASK 0x03 + +#define ADAU1701_SEROCTL_INV_LRCLK 0x2000 +#define ADAU1701_SEROCTL_INV_BCLK 0x1000 +#define ADAU1701_SEROCTL_MASTER 0x0800 + +#define ADAU1701_SEROCTL_OBF16 0x0000 +#define ADAU1701_SEROCTL_OBF8 0x0200 +#define ADAU1701_SEROCTL_OBF4 0x0400 +#define ADAU1701_SEROCTL_OBF2 0x0600 +#define ADAU1701_SEROCTL_OBF_MASK 0x0600 + +#define ADAU1701_SEROCTL_OLF1024 0x0000 +#define ADAU1701_SEROCTL_OLF512 0x0080 +#define ADAU1701_SEROCTL_OLF256 0x0100 +#define ADAU1701_SEROCTL_OLF_MASK 0x0180 + +#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000 +#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004 +#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008 +#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c +#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010 +#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c + +#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000 +#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001 +#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010 +#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003 + +#define ADAU1701_AUXNPOW_VBPD 0x40 +#define ADAU1701_AUXNPOW_VRPD 0x20 + +#define ADAU1701_SERICTL_I2S 0 +#define ADAU1701_SERICTL_LEFTJ 1 +#define ADAU1701_SERICTL_TDM 2 +#define ADAU1701_SERICTL_RIGHTJ_24 3 +#define ADAU1701_SERICTL_RIGHTJ_20 4 +#define ADAU1701_SERICTL_RIGHTJ_18 5 +#define ADAU1701_SERICTL_RIGHTJ_16 6 +#define ADAU1701_SERICTL_MODE_MASK 7 +#define ADAU1701_SERICTL_INV_BCLK BIT(3) +#define ADAU1701_SERICTL_INV_LRCLK BIT(4) + +#define ADAU1701_OSCIPOW_OPD 0x04 +#define ADAU1701_DACSET_DACINIT 1 + +#define ADAU1701_FIRMWARE "adau1701.bin" + +struct adau1701 { + unsigned int dai_fmt; +}; + +static const struct snd_kcontrol_new adau1701_controls[] = { + SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1), + SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), +}; + +static const struct snd_soc_dapm_route adau1701_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC", NULL, "IN0" }, + { "ADC", NULL, "IN1" }, +}; + +static unsigned int adau1701_register_size(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case ADAU1701_DSPCTRL: + case ADAU1701_SEROCTL: + case ADAU1701_AUXNPOW: + case ADAU1701_OSCIPOW: + case ADAU1701_DACSET: + return 2; + case ADAU1701_SERICTL: + return 1; + } + + dev_err(codec->dev, "Unsupported register address: %d\n", reg); + return 0; +} + +static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + unsigned int i; + unsigned int size; + uint8_t buf[4]; + int ret; + + size = adau1701_register_size(codec, reg); + if (size == 0) + return -EINVAL; + + snd_soc_cache_write(codec, reg, value); + + buf[0] = 0x08; + buf[1] = reg; + + for (i = size + 1; i >= 2; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2); + if (ret == size + 2) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int value; + unsigned int ret; + + ret = snd_soc_cache_read(codec, reg, &value); + if (ret) + return ret; + + return value; +} + +static int adau1701_load_firmware(struct snd_soc_codec *codec) +{ + return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE); +} + +static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, + snd_pcm_format_t format) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK; + unsigned int val; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAU1701_SEROCTL_WORD_LEN_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAU1701_SEROCTL_WORD_LEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAU1701_SEROCTL_WORD_LEN_24; + break; + default: + return -EINVAL; + } + + if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) { + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= ADAU1701_SEROCTL_MSB_DEALY16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= ADAU1701_SEROCTL_MSB_DEALY12; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= ADAU1701_SEROCTL_MSB_DEALY8; + break; + } + mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK; + } + + snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val); + + return 0; +} + +static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, + snd_pcm_format_t format) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAU1701_SERICTL_RIGHTJ_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAU1701_SERICTL_RIGHTJ_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAU1701_SERICTL_RIGHTJ_24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_SERICTL, + ADAU1701_SERICTL_MODE_MASK, val); + + return 0; +} + +static int adau1701_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + snd_pcm_format_t format; + unsigned int val; + + switch (params_rate(params)) { + case 192000: + val = ADAU1701_DSPCTRL_SR_192; + break; + case 96000: + val = ADAU1701_DSPCTRL_SR_96; + break; + case 48000: + val = ADAU1701_DSPCTRL_SR_48; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_SR_MASK, val); + + format = params_format(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return adau1701_set_playback_pcm_format(codec, format); + else + return adau1701_set_capture_pcm_format(codec, format); +} + +static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int serictl = 0x00, seroctl = 0x00; + bool invert_lrclk; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* master, 64-bits per sample, 1 frame per sample */ + seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16 + | ADAU1701_SEROCTL_OLF1024; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + serictl |= ADAU1701_SERICTL_LEFTJ; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + serictl |= ADAU1701_SERICTL_RIGHTJ_24; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY8; + invert_lrclk = !invert_lrclk; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) { + seroctl |= ADAU1701_SEROCTL_INV_LRCLK; + serictl |= ADAU1701_SERICTL_INV_LRCLK; + } + + adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + snd_soc_write(codec, ADAU1701_SERICTL, serictl); + snd_soc_update_bits(codec, ADAU1701_SEROCTL, + ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl); + + return 0; +} + +static int adau1701_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* Enable VREF and VREF buffer */ + snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00); + break; + case SND_SOC_BIAS_OFF: + /* Disable VREF and VREF buffer */ + snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int mask = ADAU1701_DSPCTRL_DAM; + unsigned int val; + + if (mute) + val = 0; + else + val = mask; + + snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val); + + return 0; +} + +static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + unsigned int val; + + switch (clk_id) { + case ADAU1701_CLK_SRC_OSC: + val = 0x0; + break; + case ADAU1701_CLK_SRC_MCLK: + val = ADAU1701_OSCIPOW_OPD; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val); + + return 0; +} + +#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops adau1701_dai_ops = { + .set_fmt = adau1701_set_dai_fmt, + .hw_params = adau1701_hw_params, + .digital_mute = adau1701_digital_mute, +}; + +static struct snd_soc_dai_driver adau1701_dai = { + .name = "adau1701", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .ops = &adau1701_dai_ops, + .symmetric_rates = 1, +}; + +static int adau1701_probe(struct snd_soc_codec *codec) +{ + int ret; + + codec->dapm.idle_bias_off = 1; + + ret = adau1701_load_firmware(codec); + if (ret) + dev_warn(codec->dev, "Failed to load firmware\n"); + + snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT); + snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR); + + return 0; +} + +static struct snd_soc_codec_driver adau1701_codec_drv = { + .probe = adau1701_probe, + .set_bias_level = adau1701_set_bias_level, + + .reg_cache_size = ADAU1701_NUM_REGS, + .reg_word_size = sizeof(u16), + + .controls = adau1701_controls, + .num_controls = ARRAY_SIZE(adau1701_controls), + .dapm_widgets = adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets), + .dapm_routes = adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes), + + .write = adau1701_write, + .read = adau1701_read, + + .set_sysclk = adau1701_set_sysclk, +}; + +static __devinit int adau1701_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adau1701 *adau1701; + int ret; + + adau1701 = kzalloc(sizeof(*adau1701), GFP_KERNEL); + if (!adau1701) + return -ENOMEM; + + i2c_set_clientdata(client, adau1701); + ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, + &adau1701_dai, 1); + if (ret < 0) + kfree(adau1701); + + return ret; +} + +static __devexit int adau1701_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id adau1701_i2c_id[] = { + { "adau1701", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); + +static struct i2c_driver adau1701_i2c_driver = { + .driver = { + .name = "adau1701", + .owner = THIS_MODULE, + }, + .probe = adau1701_i2c_probe, + .remove = __devexit_p(adau1701_i2c_remove), + .id_table = adau1701_i2c_id, +}; + +static int __init adau1701_init(void) +{ + return i2c_add_driver(&adau1701_i2c_driver); +} +module_init(adau1701_init); + +static void __exit adau1701_exit(void) +{ + i2c_del_driver(&adau1701_i2c_driver); +} +module_exit(adau1701_exit); + +MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver"); +MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h new file mode 100644 index 000000000000..8d0949a2aec9 --- /dev/null +++ b/sound/soc/codecs/adau1701.h @@ -0,0 +1,17 @@ +/* + * header file for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADAU1701_H +#define _ADAU1701_H + +enum adau1701_clk_src { + ADAU1701_CLK_SRC_OSC, + ADAU1701_CLK_SRC_MCLK, +}; + +#endif diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c new file mode 100644 index 000000000000..300c04b70e71 --- /dev/null +++ b/sound/soc/codecs/adav80x.c @@ -0,0 +1,951 @@ +/* + * ADAV80X Audio Codec driver supporting ADAV801, ADAV803 + * + * Copyright 2011 Analog Devices Inc. + * Author: Yi Li <yi.li@analog.com> + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> + +#include "adav80x.h" + +#define ADAV80X_PLAYBACK_CTRL 0x04 +#define ADAV80X_AUX_IN_CTRL 0x05 +#define ADAV80X_REC_CTRL 0x06 +#define ADAV80X_AUX_OUT_CTRL 0x07 +#define ADAV80X_DPATH_CTRL1 0x62 +#define ADAV80X_DPATH_CTRL2 0x63 +#define ADAV80X_DAC_CTRL1 0x64 +#define ADAV80X_DAC_CTRL2 0x65 +#define ADAV80X_DAC_CTRL3 0x66 +#define ADAV80X_DAC_L_VOL 0x68 +#define ADAV80X_DAC_R_VOL 0x69 +#define ADAV80X_PGA_L_VOL 0x6c +#define ADAV80X_PGA_R_VOL 0x6d +#define ADAV80X_ADC_CTRL1 0x6e +#define ADAV80X_ADC_CTRL2 0x6f +#define ADAV80X_ADC_L_VOL 0x70 +#define ADAV80X_ADC_R_VOL 0x71 +#define ADAV80X_PLL_CTRL1 0x74 +#define ADAV80X_PLL_CTRL2 0x75 +#define ADAV80X_ICLK_CTRL1 0x76 +#define ADAV80X_ICLK_CTRL2 0x77 +#define ADAV80X_PLL_CLK_SRC 0x78 +#define ADAV80X_PLL_OUTE 0x7a + +#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00 +#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll)) +#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll)) + +#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5) +#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2) +#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src) +#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3) + +#define ADAV80X_PLL_CTRL1_PLLDIV 0x10 +#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll)) +#define ADAV80X_PLL_CTRL1_XTLPD 0x02 + +#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4)) + +#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00) +#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08) +#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c) + +#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02) +#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01) +#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f) + +#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80 +#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00 +#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80 + +#define ADAV80X_DAC_CTRL1_PD 0x80 + +#define ADAV80X_DAC_CTRL2_DIV1 0x00 +#define ADAV80X_DAC_CTRL2_DIV1_5 0x10 +#define ADAV80X_DAC_CTRL2_DIV2 0x20 +#define ADAV80X_DAC_CTRL2_DIV3 0x30 +#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30 + +#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00 +#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40 +#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80 +#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0 + +#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00 +#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01 +#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02 +#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03 +#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01 + +#define ADAV80X_CAPTURE_MODE_MASTER 0x20 +#define ADAV80X_CAPTURE_WORD_LEN24 0x00 +#define ADAV80X_CAPTURE_WORD_LEN20 0x04 +#define ADAV80X_CAPTRUE_WORD_LEN18 0x08 +#define ADAV80X_CAPTURE_WORD_LEN16 0x0c +#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c + +#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00 +#define ADAV80X_CAPTURE_MODE_I2S 0x01 +#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03 +#define ADAV80X_CAPTURE_MODE_MASK 0x03 + +#define ADAV80X_PLAYBACK_MODE_MASTER 0x10 +#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00 +#define ADAV80X_PLAYBACK_MODE_I2S 0x01 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07 +#define ADAV80X_PLAYBACK_MODE_MASK 0x07 + +#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x)) + +static u8 adav80x_default_regs[] = { + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x80, 0x26, 0x00, 0x00, + 0x02, 0x40, 0x20, 0x00, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x92, 0xb1, 0x37, + 0x48, 0xd2, 0xfb, 0xca, 0xd2, 0x15, 0xe8, 0x29, 0xb9, 0x6a, 0xda, 0x2b, + 0xb7, 0xc0, 0x11, 0x65, 0x5c, 0xf6, 0xff, 0x8d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, + 0x00, 0xe8, 0x46, 0xe1, 0x5b, 0xd3, 0x43, 0x77, 0x93, 0xa7, 0x44, 0xee, + 0x32, 0x12, 0xc0, 0x11, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, +}; + +struct adav80x { + enum snd_soc_control_type control_type; + + enum adav80x_clk_src clk_src; + unsigned int sysclk; + enum adav80x_pll_src pll_src; + + unsigned int dai_fmt[2]; + unsigned int rate; + bool deemph; + bool sysclk_pd[3]; +}; + +static const char *adav80x_mux_text[] = { + "ADC", + "Playback", + "Aux Playback", +}; + +static const unsigned int adav80x_mux_values[] = { + 0, 2, 3, +}; + +#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \ + SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \ + ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \ + adav80x_mux_values) + +static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0); +static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3); +static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3); + +static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl = + SOC_DAPM_VALUE_ENUM("Route", adav80x_aux_capture_enum); +static const struct snd_kcontrol_new adav80x_capture_mux_ctrl = + SOC_DAPM_VALUE_ENUM("Route", adav80x_capture_enum); +static const struct snd_kcontrol_new adav80x_dac_mux_ctrl = + SOC_DAPM_VALUE_ENUM("Route", adav80x_dac_enum); + +#define ADAV80X_MUX(name, ctrl) \ + SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1), + SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1), + + SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0), + + ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl), + ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl), + ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl), + + SND_SOC_DAPM_INPUT("VINR"), + SND_SOC_DAPM_INPUT("VINL"), + SND_SOC_DAPM_OUTPUT("VOUTR"), + SND_SOC_DAPM_OUTPUT("VOUTL"), + + SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0), +}; + +static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = source->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + const char *clk; + + switch (adav80x->clk_src) { + case ADAV80X_CLK_PLL1: + clk = "PLL1"; + break; + case ADAV80X_CLK_PLL2: + clk = "PLL2"; + break; + case ADAV80X_CLK_XTAL: + clk = "OSC"; + break; + default: + return 0; + } + + return strcmp(source->name, clk) == 0; +} + +static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = source->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL; +} + + +static const struct snd_soc_dapm_route adav80x_dapm_routes[] = { + { "DAC Select", "ADC", "ADC" }, + { "DAC Select", "Playback", "AIFIN" }, + { "DAC Select", "Aux Playback", "AIFAUXIN" }, + { "DAC", NULL, "DAC Select" }, + + { "Capture Select", "ADC", "ADC" }, + { "Capture Select", "Playback", "AIFIN" }, + { "Capture Select", "Aux Playback", "AIFAUXIN" }, + { "AIFOUT", NULL, "Capture Select" }, + + { "Aux Capture Select", "ADC", "ADC" }, + { "Aux Capture Select", "Playback", "AIFIN" }, + { "Aux Capture Select", "Aux Playback", "AIFAUXIN" }, + { "AIFAUXOUT", NULL, "Aux Capture Select" }, + + { "VOUTR", NULL, "DAC" }, + { "VOUTL", NULL, "DAC" }, + + { "Left PGA", NULL, "VINL" }, + { "Right PGA", NULL, "VINR" }, + { "ADC", NULL, "Left PGA" }, + { "ADC", NULL, "Right PGA" }, + + { "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check }, + { "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check }, + { "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check }, + { "PLL1", NULL, "OSC", adav80x_dapm_pll_check }, + { "PLL2", NULL, "OSC", adav80x_dapm_pll_check }, + + { "ADC", NULL, "SYSCLK" }, + { "DAC", NULL, "SYSCLK" }, + { "AIFOUT", NULL, "SYSCLK" }, + { "AIFAUXOUT", NULL, "SYSCLK" }, + { "AIFIN", NULL, "SYSCLK" }, + { "AIFAUXIN", NULL, "SYSCLK" }, +}; + +static int adav80x_set_deemph(struct snd_soc_codec *codec) +{ + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (adav80x->deemph) { + switch (adav80x->rate) { + case 32000: + val = ADAV80X_DAC_CTRL2_DEEMPH_32; + break; + case 44100: + val = ADAV80X_DAC_CTRL2_DEEMPH_44; + break; + case 48000: + case 64000: + case 88200: + case 96000: + val = ADAV80X_DAC_CTRL2_DEEMPH_48; + break; + default: + val = ADAV80X_DAC_CTRL2_DEEMPH_NONE; + break; + } + } else { + val = ADAV80X_DAC_CTRL2_DEEMPH_NONE; + } + + return snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2, + ADAV80X_DAC_CTRL2_DEEMPH_MASK, val); +} + +static int adav80x_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int deemph = ucontrol->value.enumerated.item[0]; + + if (deemph > 1) + return -EINVAL; + + adav80x->deemph = deemph; + + return adav80x_set_deemph(codec); +} + +static int adav80x_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = adav80x->deemph; + return 0; +}; + +static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0); + +static const struct snd_kcontrol_new adav80x_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL, + ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv), + SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL, + ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv), + + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL, + ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv), + + SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0), + SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1), + + SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0), + + SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0, + adav80x_get_deemph, adav80x_put_deemph), +}; + +static unsigned int adav80x_port_ctrl_regs[2][2] = { + { ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, }, + { ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL }, +}; + +static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int capture = 0x00; + unsigned int playback = 0x00; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + capture |= ADAV80X_CAPTURE_MODE_MASTER; + playback |= ADAV80X_PLAYBACK_MODE_MASTER; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + capture |= ADAV80X_CAPTURE_MODE_I2S; + playback |= ADAV80X_PLAYBACK_MODE_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + capture |= ADAV80X_CAPTURE_MODE_LEFT_J; + playback |= ADAV80X_PLAYBACK_MODE_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + capture |= ADAV80X_CAPTURE_MODE_RIGHT_J; + playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0], + ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER, + capture); + snd_soc_write(codec, adav80x_port_ctrl_regs[dai->id][1], playback); + + adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int adav80x_set_adc_clock(struct snd_soc_codec *codec, + unsigned int sample_rate) +{ + unsigned int val; + + if (sample_rate <= 48000) + val = ADAV80X_ADC_CTRL1_MODULATOR_128FS; + else + val = ADAV80X_ADC_CTRL1_MODULATOR_64FS; + + snd_soc_update_bits(codec, ADAV80X_ADC_CTRL1, + ADAV80X_ADC_CTRL1_MODULATOR_MASK, val); + + return 0; +} + +static int adav80x_set_dac_clock(struct snd_soc_codec *codec, + unsigned int sample_rate) +{ + unsigned int val; + + if (sample_rate <= 48000) + val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS; + else + val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS; + + snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2, + ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK, + val); + + return 0; +} + +static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec, + struct snd_soc_dai *dai, snd_pcm_format_t format) +{ + unsigned int val; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAV80X_CAPTURE_WORD_LEN16; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + val = ADAV80X_CAPTRUE_WORD_LEN18; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAV80X_CAPTURE_WORD_LEN20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAV80X_CAPTURE_WORD_LEN24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0], + ADAV80X_CAPTURE_WORD_LEN_MASK, val); + + return 0; +} + +static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec, + struct snd_soc_dai *dai, snd_pcm_format_t format) +{ + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][1], + ADAV80X_PLAYBACK_MODE_MASK, val); + + return 0; +} + +static int adav80x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + + if (rate * 256 != adav80x->sysclk) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + adav80x_set_playback_pcm_format(codec, dai, + params_format(params)); + adav80x_set_dac_clock(codec, rate); + } else { + adav80x_set_capture_pcm_format(codec, dai, + params_format(params)); + adav80x_set_adc_clock(codec, rate); + } + adav80x->rate = rate; + adav80x_set_deemph(codec); + + return 0; +} + +static int adav80x_set_sysclk(struct snd_soc_codec *codec, + int clk_id, unsigned int freq, int dir) +{ + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + if (dir == SND_SOC_CLOCK_IN) { + switch (clk_id) { + case ADAV80X_CLK_XIN: + case ADAV80X_CLK_XTAL: + case ADAV80X_CLK_MCLKI: + case ADAV80X_CLK_PLL1: + case ADAV80X_CLK_PLL2: + break; + default: + return -EINVAL; + } + + adav80x->sysclk = freq; + + if (adav80x->clk_src != clk_id) { + unsigned int iclk_ctrl1, iclk_ctrl2; + + adav80x->clk_src = clk_id; + if (clk_id == ADAV80X_CLK_XTAL) + clk_id = ADAV80X_CLK_XIN; + + iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) | + ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) | + ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id); + iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id); + + snd_soc_write(codec, ADAV80X_ICLK_CTRL1, iclk_ctrl1); + snd_soc_write(codec, ADAV80X_ICLK_CTRL2, iclk_ctrl2); + + snd_soc_dapm_sync(&codec->dapm); + } + } else { + unsigned int mask; + + switch (clk_id) { + case ADAV80X_CLK_SYSCLK1: + case ADAV80X_CLK_SYSCLK2: + case ADAV80X_CLK_SYSCLK3: + break; + default: + return -EINVAL; + } + + clk_id -= ADAV80X_CLK_SYSCLK1; + mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id); + + if (freq == 0) { + snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, mask); + adav80x->sysclk_pd[clk_id] = true; + } else { + snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, 0); + adav80x->sysclk_pd[clk_id] = false; + } + + if (adav80x->sysclk_pd[0]) + snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); + else + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); + + if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2]) + snd_soc_dapm_disable_pin(&codec->dapm, "PLL2"); + else + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); + + snd_soc_dapm_sync(&codec->dapm); + } + + return 0; +} + +static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + unsigned int pll_ctrl1 = 0; + unsigned int pll_ctrl2 = 0; + unsigned int pll_src; + + switch (source) { + case ADAV80X_PLL_SRC_XTAL: + case ADAV80X_PLL_SRC_XIN: + case ADAV80X_PLL_SRC_MCLKI: + break; + default: + return -EINVAL; + } + + if (!freq_out) + return 0; + + switch (freq_in) { + case 27000000: + break; + case 54000000: + if (source == ADAV80X_PLL_SRC_XIN) { + pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV; + break; + } + default: + return -EINVAL; + } + + if (freq_out > 12288000) { + pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id); + freq_out /= 2; + } + + /* freq_out = sample_rate * 256 */ + switch (freq_out) { + case 8192000: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id); + break; + case 11289600: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id); + break; + case 12288000: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAV80X_PLL_CTRL1, ADAV80X_PLL_CTRL1_PLLDIV, + pll_ctrl1); + snd_soc_update_bits(codec, ADAV80X_PLL_CTRL2, + ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2); + + if (source != adav80x->pll_src) { + if (source == ADAV80X_PLL_SRC_MCLKI) + pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id); + else + pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id); + + snd_soc_update_bits(codec, ADAV80X_PLL_CLK_SRC, + ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src); + + adav80x->pll_src = source; + + snd_soc_dapm_sync(&codec->dapm); + } + + return 0; +} + +static int adav80x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + unsigned int mask = ADAV80X_DAC_CTRL1_PD; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, 0x00); + break; + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, mask); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +/* Enforce the same sample rate on all audio interfaces */ +static int adav80x_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + if (!codec->active || !adav80x->rate) + return 0; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate); +} + +static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + if (!codec->active) + adav80x->rate = 0; +} + +static const struct snd_soc_dai_ops adav80x_dai_ops = { + .set_fmt = adav80x_set_dai_fmt, + .hw_params = adav80x_hw_params, + .startup = adav80x_dai_startup, + .shutdown = adav80x_dai_shutdown, +}; + +#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) + +#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + +#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver adav80x_dais[] = { + { + .name = "adav80x-hifi", + .id = 0, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_PLAYBACK_RATES, + .formats = ADAV80X_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_CAPTURE_RATES, + .formats = ADAV80X_FORMATS, + }, + .ops = &adav80x_dai_ops, + }, + { + .name = "adav80x-aux", + .id = 1, + .playback = { + .stream_name = "Aux Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_PLAYBACK_RATES, + .formats = ADAV80X_FORMATS, + }, + .capture = { + .stream_name = "Aux Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_CAPTURE_RATES, + .formats = ADAV80X_FORMATS, + }, + .ops = &adav80x_dai_ops, + }, +}; + +static int adav80x_probe(struct snd_soc_codec *codec) +{ + int ret; + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + ret = snd_soc_codec_set_cache_io(codec, 7, 9, adav80x->control_type); + if (ret) { + dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); + return ret; + } + + /* Force PLLs on for SYSCLK output */ + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); + + /* Power down S/PDIF receiver, since it is currently not supported */ + snd_soc_write(codec, ADAV80X_PLL_OUTE, 0x20); + /* Disable DAC zero flag */ + snd_soc_write(codec, ADAV80X_DAC_CTRL3, 0x6); + + return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +} + +static int adav80x_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static int adav80x_resume(struct snd_soc_codec *codec) +{ + adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + codec->cache_sync = 1; + snd_soc_cache_sync(codec); + + return 0; +} + +static int adav80x_remove(struct snd_soc_codec *codec) +{ + return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static struct snd_soc_codec_driver adav80x_codec_driver = { + .probe = adav80x_probe, + .remove = adav80x_remove, + .suspend = adav80x_suspend, + .resume = adav80x_resume, + .set_bias_level = adav80x_set_bias_level, + + .set_pll = adav80x_set_pll, + .set_sysclk = adav80x_set_sysclk, + + .reg_word_size = sizeof(u8), + .reg_cache_size = ARRAY_SIZE(adav80x_default_regs), + .reg_cache_default = adav80x_default_regs, + + .controls = adav80x_controls, + .num_controls = ARRAY_SIZE(adav80x_controls), + .dapm_widgets = adav80x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets), + .dapm_routes = adav80x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), +}; + +static int __devinit adav80x_bus_probe(struct device *dev, + enum snd_soc_control_type control_type) +{ + struct adav80x *adav80x; + int ret; + + adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL); + if (!adav80x) + return -ENOMEM; + + dev_set_drvdata(dev, adav80x); + adav80x->control_type = control_type; + + ret = snd_soc_register_codec(dev, &adav80x_codec_driver, + adav80x_dais, ARRAY_SIZE(adav80x_dais)); + if (ret) + kfree(adav80x); + + return ret; +} + +static int __devexit adav80x_bus_remove(struct device *dev) +{ + snd_soc_unregister_codec(dev); + kfree(dev_get_drvdata(dev)); + return 0; +} + +#if defined(CONFIG_SPI_MASTER) +static int __devinit adav80x_spi_probe(struct spi_device *spi) +{ + return adav80x_bus_probe(&spi->dev, SND_SOC_SPI); +} + +static int __devexit adav80x_spi_remove(struct spi_device *spi) +{ + return adav80x_bus_remove(&spi->dev); +} + +static struct spi_driver adav80x_spi_driver = { + .driver = { + .name = "adav801", + .owner = THIS_MODULE, + }, + .probe = adav80x_spi_probe, + .remove = __devexit_p(adav80x_spi_remove), +}; +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static const struct i2c_device_id adav80x_id[] = { + { "adav803", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adav80x_id); + +static int __devinit adav80x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return adav80x_bus_probe(&client->dev, SND_SOC_I2C); +} + +static int __devexit adav80x_i2c_remove(struct i2c_client *client) +{ + return adav80x_bus_remove(&client->dev); +} + +static struct i2c_driver adav80x_i2c_driver = { + .driver = { + .name = "adav803", + .owner = THIS_MODULE, + }, + .probe = adav80x_i2c_probe, + .remove = __devexit_p(adav80x_i2c_remove), + .id_table = adav80x_id, +}; +#endif + +static int __init adav80x_init(void) +{ + int ret = 0; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&adav80x_i2c_driver); + if (ret) + return ret; +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&adav80x_spi_driver); +#endif + + return ret; +} +module_init(adav80x_init); + +static void __exit adav80x_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&adav80x_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&adav80x_spi_driver); +#endif +} +module_exit(adav80x_exit); + +MODULE_DESCRIPTION("ASoC ADAV80x driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_AUTHOR("Yi Li <yi.li@analog.com>>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h new file mode 100644 index 000000000000..adb0fc76d4e3 --- /dev/null +++ b/sound/soc/codecs/adav80x.h @@ -0,0 +1,35 @@ +/* + * header file for ADAV80X parts + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADAV80X_H +#define _ADAV80X_H + +enum adav80x_pll_src { + ADAV80X_PLL_SRC_XIN, + ADAV80X_PLL_SRC_XTAL, + ADAV80X_PLL_SRC_MCLKI, +}; + +enum adav80x_pll { + ADAV80X_PLL1 = 0, + ADAV80X_PLL2 = 1, +}; + +enum adav80x_clk_src { + ADAV80X_CLK_XIN = 0, + ADAV80X_CLK_MCLKI = 1, + ADAV80X_CLK_PLL1 = 2, + ADAV80X_CLK_PLL2 = 3, + ADAV80X_CLK_XTAL = 6, + + ADAV80X_CLK_SYSCLK1 = 6, + ADAV80X_CLK_SYSCLK2 = 7, + ADAV80X_CLK_SYSCLK3 = 8, +}; + +#endif diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index ed96f247c2da..7a64e58cddc4 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -457,7 +457,7 @@ static struct snd_soc_dai_ops ak4641_pcm_dai_ops = { .set_sysclk = ak4641_set_dai_sysclk, }; -struct snd_soc_dai_driver ak4641_dai[] = { +static struct snd_soc_dai_driver ak4641_dai[] = { { .name = "ak4641-hifi", .id = 1, diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 0206a17d7283..6cc8678f49f3 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -636,10 +636,7 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec) #endif /* CONFIG_PM */ /* - * ASoC codec device structure - * - * Assign this variable to the codec_dev field of the machine driver's - * snd_soc_device structure. + * ASoC codec driver structure */ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { .probe = cs4270_probe, diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 4173b67c94d1..ac65a2d36408 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1397,8 +1397,6 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai, if (freq == max98088->sysclk) return 0; - max98088->sysclk = freq; /* remember current sysclk */ - /* Setup clocks for slave mode, and using the PLL * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) * 0x02 (when master clk is 20MHz to 30MHz).. diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index e1d282d477da..668434d44303 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1517,8 +1517,6 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai, if (freq == max98095->sysclk) return 0; - max98095->sysclk = freq; /* remember current sysclk */ - /* Setup clocks for slave mode, and using the PLL * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) * 0x02 (when master clk is 20MHz to 40MHz).. @@ -2261,11 +2259,11 @@ static int max98095_probe(struct snd_soc_codec *codec) ret = snd_soc_read(codec, M98095_0FF_REV_ID); if (ret < 0) { - dev_err(codec->dev, "Failed to read device revision: %d\n", + dev_err(codec->dev, "Failure reading hardware revision: %d\n", ret); goto err_access; } - dev_info(codec->dev, "revision %c\n", ret + 'A'); + dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A'); snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV); @@ -2342,8 +2340,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c, max98095->control_data = i2c; max98095->pdata = i2c->dev.platform_data; - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_max98095, &max98095_dai[0], 3); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095, + max98095_dai, ARRAY_SIZE(max98095_dai)); if (ret < 0) kfree(max98095); return ret; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index ff29380c9ed3..76258f2a2ffb 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -907,6 +907,7 @@ static int ldo_regulator_register(struct snd_soc_codec *codec, struct regulator_init_data *init_data, int voltage) { + dev_err(codec->dev, "this setup needs regulator support in the kernel\n"); return -EINVAL; } @@ -1218,6 +1219,34 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) return 0; } +static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int ret; + + /* set internal ldo to 1.2v */ + ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE); + if (ret) { + dev_err(codec->dev, + "Failed to register vddd internal supplies: %d\n", ret); + return ret; + } + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + dev_info(codec->dev, "Using internal LDO instead of VDDD\n"); + return 0; +} + static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { u16 reg; @@ -1235,30 +1264,9 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) if (!ret) external_vddd = 1; else { - /* set internal ldo to 1.2v */ - int voltage = LDO_VOLTAGE; - - ret = ldo_regulator_register(codec, &ldo_init_data, voltage); - if (ret) { - dev_err(codec->dev, - "Failed to register vddd internal supplies: %d\n", - ret); - return ret; - } - - sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; - - ret = regulator_bulk_get(codec->dev, - ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - - if (ret) { - ldo_regulator_remove(codec); - dev_err(codec->dev, - "Failed to request supplies: %d\n", ret); - + ret = sgtl5000_replace_vddd_with_ldo(codec); + if (ret) return ret; - } } ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), @@ -1287,7 +1295,6 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) * roll back to use internal LDO */ if (external_vddd && rev >= 0x11) { - int voltage = LDO_VOLTAGE; /* disable all regulator first */ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); @@ -1295,23 +1302,10 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); - ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + ret = sgtl5000_replace_vddd_with_ldo(codec); if (ret) return ret; - sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; - - ret = regulator_bulk_get(codec->dev, - ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (ret) { - ldo_regulator_remove(codec); - dev_err(codec->dev, - "Failed to request supplies: %d\n", ret); - - return ret; - } - ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); if (ret) diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c new file mode 100644 index 000000000000..409d89d1f34c --- /dev/null +++ b/sound/soc/codecs/sta32x.c @@ -0,0 +1,917 @@ +/* + * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach <js@sig21.net> + * + * based on code from: + * Wolfson Microelectronics PLC. + * Mark Brown <broonie@opensource.wolfsonmicro.com> + * Freescale Semiconductor, Inc. + * Timur Tabi <timur@freescale.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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "sta32x.h" + +#define STA32X_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define STA32X_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + +/* Power-up register defaults */ +static const u8 sta32x_regs[STA32X_REGISTER_COUNT] = { + 0x63, 0x80, 0xc2, 0x40, 0xc2, 0x5c, 0x10, 0xff, 0x60, 0x60, + 0x60, 0x80, 0x00, 0x00, 0x00, 0x40, 0x80, 0x77, 0x6a, 0x69, + 0x6a, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, + 0xc0, 0xf3, 0x33, 0x00, 0x0c, +}; + +/* regulator power supply names */ +static const char *sta32x_supply_names[] = { + "Vdda", /* analog supply, 3.3VV */ + "Vdd3", /* digital supply, 3.3V */ + "Vcc" /* power amp spply, 10V - 36V */ +}; + +/* codec private data */ +struct sta32x_priv { + struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)]; + struct snd_soc_codec *codec; + + unsigned int mclk; + unsigned int format; +}; + +static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0); + +static const char *sta32x_drc_ac[] = { + "Anti-Clipping", "Dynamic Range Compression" }; +static const char *sta32x_auto_eq_mode[] = { + "User", "Preset", "Loudness" }; +static const char *sta32x_auto_gc_mode[] = { + "User", "AC no clipping", "AC limited clipping (10%)", + "DRC nighttime listening mode" }; +static const char *sta32x_auto_xo_mode[] = { + "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz", + "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" }; +static const char *sta32x_preset_eq_mode[] = { + "Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft", + "Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1", + "Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2", + "Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7", + "Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12", + "Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" }; +static const char *sta32x_limiter_select[] = { + "Limiter Disabled", "Limiter #1", "Limiter #2" }; +static const char *sta32x_limiter_attack_rate[] = { + "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", + "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", + "0.0645", "0.0564", "0.0501", "0.0451" }; +static const char *sta32x_limiter_release_rate[] = { + "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", + "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", + "0.0134", "0.0117", "0.0110", "0.0104" }; + +static const unsigned int sta32x_limiter_ac_attack_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), +}; + +static const unsigned int sta32x_limiter_ac_release_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), + 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), +}; + +static const unsigned int sta32x_limiter_drc_attack_tlv[] = { + TLV_DB_RANGE_HEAD(3), + 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), + 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), +}; + +static const unsigned int sta32x_limiter_drc_release_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), + 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), + 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), + 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), +}; + +static const struct soc_enum sta32x_drc_ac_enum = + SOC_ENUM_SINGLE(STA32X_CONFD, STA32X_CONFD_DRC_SHIFT, + 2, sta32x_drc_ac); +static const struct soc_enum sta32x_auto_eq_enum = + SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT, + 3, sta32x_auto_eq_mode); +static const struct soc_enum sta32x_auto_gc_enum = + SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT, + 4, sta32x_auto_gc_mode); +static const struct soc_enum sta32x_auto_xo_enum = + SOC_ENUM_SINGLE(STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT, + 16, sta32x_auto_xo_mode); +static const struct soc_enum sta32x_preset_eq_enum = + SOC_ENUM_SINGLE(STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT, + 32, sta32x_preset_eq_mode); +static const struct soc_enum sta32x_limiter_ch1_enum = + SOC_ENUM_SINGLE(STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT, + 3, sta32x_limiter_select); +static const struct soc_enum sta32x_limiter_ch2_enum = + SOC_ENUM_SINGLE(STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT, + 3, sta32x_limiter_select); +static const struct soc_enum sta32x_limiter_ch3_enum = + SOC_ENUM_SINGLE(STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT, + 3, sta32x_limiter_select); +static const struct soc_enum sta32x_limiter1_attack_rate_enum = + SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxA_SHIFT, + 16, sta32x_limiter_attack_rate); +static const struct soc_enum sta32x_limiter2_attack_rate_enum = + SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxA_SHIFT, + 16, sta32x_limiter_attack_rate); +static const struct soc_enum sta32x_limiter1_release_rate_enum = + SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxR_SHIFT, + 16, sta32x_limiter_release_rate); +static const struct soc_enum sta32x_limiter2_release_rate_enum = + SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxR_SHIFT, + 16, sta32x_limiter_release_rate); + +/* byte array controls for setting biquad, mixer, scaling coefficients; + * for biquads all five coefficients need to be set in one go, + * mixer and pre/postscale coefs can be set individually; + * each coef is 24bit, the bytes are ordered in the same way + * as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0) + */ + +static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 3 * numcoef; + return 0; +} + +static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA32X_CFUD */ + cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0; + /* chip documentation does not say if the bits are self clearing, + * so do it explicitly */ + snd_soc_write(codec, STA32X_CFUD, cfud); + + snd_soc_write(codec, STA32X_CFADDR2, index); + if (numcoef == 1) + snd_soc_write(codec, STA32X_CFUD, cfud | 0x04); + else if (numcoef == 5) + snd_soc_write(codec, STA32X_CFUD, cfud | 0x08); + else + return -EINVAL; + for (i = 0; i < 3 * numcoef; i++) + ucontrol->value.bytes.data[i] = + snd_soc_read(codec, STA32X_B1CF1 + i); + + return 0; +} + +static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA32X_CFUD */ + cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0; + /* chip documentation does not say if the bits are self clearing, + * so do it explicitly */ + snd_soc_write(codec, STA32X_CFUD, cfud); + + snd_soc_write(codec, STA32X_CFADDR2, index); + for (i = 0; i < 3 * numcoef; i++) + snd_soc_write(codec, STA32X_B1CF1 + i, + ucontrol->value.bytes.data[i]); + if (numcoef == 1) + snd_soc_write(codec, STA32X_CFUD, cfud | 0x01); + else if (numcoef == 5) + snd_soc_write(codec, STA32X_CFUD, cfud | 0x02); + else + return -EINVAL; + + return 0; +} + +#define SINGLE_COEF(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta32x_coefficient_info, \ + .get = sta32x_coefficient_get,\ + .put = sta32x_coefficient_put, \ + .private_value = index | (1 << 16) } + +#define BIQUAD_COEFS(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta32x_coefficient_info, \ + .get = sta32x_coefficient_get,\ + .put = sta32x_coefficient_put, \ + .private_value = index | (5 << 16) } + +static const struct snd_kcontrol_new sta32x_snd_controls[] = { +SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv), +SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1), +SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1), +SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1), +SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1), +SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0), +SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum), +SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0), +SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0), +SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0), +SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0), +SOC_ENUM("Automode EQ", sta32x_auto_eq_enum), +SOC_ENUM("Automode GC", sta32x_auto_gc_enum), +SOC_ENUM("Automode XO", sta32x_auto_xo_enum), +SOC_ENUM("Preset EQ", sta32x_preset_eq_enum), +SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum), +SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum), +SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum), +SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv), +SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv), +SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum), +SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum), +SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum), + +/* depending on mode, the attack/release thresholds have + * two different enum definitions; provide both + */ +SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_drc_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_drc_release_tlv), + +BIQUAD_COEFS("Ch1 - Biquad 1", 0), +BIQUAD_COEFS("Ch1 - Biquad 2", 5), +BIQUAD_COEFS("Ch1 - Biquad 3", 10), +BIQUAD_COEFS("Ch1 - Biquad 4", 15), +BIQUAD_COEFS("Ch2 - Biquad 1", 20), +BIQUAD_COEFS("Ch2 - Biquad 2", 25), +BIQUAD_COEFS("Ch2 - Biquad 3", 30), +BIQUAD_COEFS("Ch2 - Biquad 4", 35), +BIQUAD_COEFS("High-pass", 40), +BIQUAD_COEFS("Low-pass", 45), +SINGLE_COEF("Ch1 - Prescale", 50), +SINGLE_COEF("Ch2 - Prescale", 51), +SINGLE_COEF("Ch1 - Postscale", 52), +SINGLE_COEF("Ch2 - Postscale", 53), +SINGLE_COEF("Ch3 - Postscale", 54), +SINGLE_COEF("Thermal warning - Postscale", 55), +SINGLE_COEF("Ch1 - Mix 1", 56), +SINGLE_COEF("Ch1 - Mix 2", 57), +SINGLE_COEF("Ch2 - Mix 1", 58), +SINGLE_COEF("Ch2 - Mix 2", 59), +SINGLE_COEF("Ch3 - Mix 1", 60), +SINGLE_COEF("Ch3 - Mix 2", 61), +}; + +static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LEFT"), +SND_SOC_DAPM_OUTPUT("RIGHT"), +SND_SOC_DAPM_OUTPUT("SUB"), +}; + +static const struct snd_soc_dapm_route sta32x_dapm_routes[] = { + { "LEFT", NULL, "DAC" }, + { "RIGHT", NULL, "DAC" }, + { "SUB", NULL, "DAC" }, +}; + +/* MCLK interpolation ratio per fs */ +static struct { + int fs; + int ir; +} interpolation_ratios[] = { + { 32000, 0 }, + { 44100, 0 }, + { 48000, 0 }, + { 88200, 1 }, + { 96000, 1 }, + { 176400, 2 }, + { 192000, 2 }, +}; + +/* MCLK to fs clock ratios */ +static struct { + int ratio; + int mcs; +} mclk_ratios[3][7] = { + { { 768, 0 }, { 512, 1 }, { 384, 2 }, { 256, 3 }, + { 128, 4 }, { 576, 5 }, { 0, 0 } }, + { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } }, + { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } }, +}; + + +/** + * sta32x_set_dai_sysclk - configure MCLK + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * The value of MCLK is used to determine which sample rates are supported + * by the STA32X, based on the mclk_ratios table. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + * + * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause + * theoretically possible sample rates to be enabled. Call it again with a + * proper value set one the external clock is set (most probably you would do + * that from a machine's driver 'hw_param' hook. + */ +static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + int i, j, ir, fs; + unsigned int rates = 0; + unsigned int rate_min = -1; + unsigned int rate_max = 0; + + pr_debug("mclk=%u\n", freq); + sta32x->mclk = freq; + + if (sta32x->mclk) { + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { + ir = interpolation_ratios[i].ir; + fs = interpolation_ratios[i].fs; + for (j = 0; mclk_ratios[ir][j].ratio; j++) { + if (mclk_ratios[ir][j].ratio * fs == freq) { + rates |= snd_pcm_rate_to_rate_bit(fs); + if (fs < rate_min) + rate_min = fs; + if (fs > rate_max) + rate_max = fs; + } + } + } + /* FIXME: soc should support a rate list */ + rates &= ~SNDRV_PCM_RATE_KNOT; + + if (!rates) { + dev_err(codec->dev, "could not find a valid sample rate\n"); + return -EINVAL; + } + } else { + /* enable all possible rates */ + rates = STA32X_RATES; + rate_min = 32000; + rate_max = 192000; + } + + codec_dai->driver->playback.rates = rates; + codec_dai->driver->playback.rate_min = rate_min; + codec_dai->driver->playback.rate_max = rate_max; + return 0; +} + +/** + * sta32x_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @fmt: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + */ +static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + u8 confb = snd_soc_read(codec, STA32X_CONFB); + + pr_debug("\n"); + confb &= ~(STA32X_CONFB_C1IM | STA32X_CONFB_C2IM); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + confb |= STA32X_CONFB_C2IM; + break; + case SND_SOC_DAIFMT_NB_IF: + confb |= STA32X_CONFB_C1IM; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, STA32X_CONFB, confb); + return 0; +} + +/** + * sta32x_hw_params - program the STA32X with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + */ +static int sta32x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + unsigned int rate; + int i, mcs = -1, ir = -1; + u8 confa, confb; + + rate = params_rate(params); + pr_debug("rate: %u\n", rate); + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) + if (interpolation_ratios[i].fs == rate) + ir = interpolation_ratios[i].ir; + if (ir < 0) + return -EINVAL; + for (i = 0; mclk_ratios[ir][i].ratio; i++) + if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) + mcs = mclk_ratios[ir][i].mcs; + if (mcs < 0) + return -EINVAL; + + confa = snd_soc_read(codec, STA32X_CONFA); + confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK); + confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT); + + confb = snd_soc_read(codec, STA32X_CONFB); + confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + pr_debug("24bit\n"); + /* fall through */ + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + pr_debug("24bit or 32bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x2; + break; + } + + break; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + pr_debug("20bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x4; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x5; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x6; + break; + } + + break; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + pr_debug("18bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x8; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x9; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xa; + break; + } + + break; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + pr_debug("16bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0xd; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xe; + break; + } + + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, STA32X_CONFA, confa); + snd_soc_write(codec, STA32X_CONFB, confb); + return 0; +} + +/** + * sta32x_set_bias_level - DAPM callback + * @codec: the codec device + * @level: DAPM power level + * + * This is called by ALSA to put the codec into low power mode + * or to wake it up. If the codec is powered off completely + * all registers must be restored after power on. + */ +static int sta32x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + + pr_debug("level = %d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + snd_soc_update_bits(codec, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + snd_soc_cache_sync(codec); + } + + /* Power up to mute */ + /* FIXME */ + snd_soc_update_bits(codec, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us. */ + snd_soc_update_bits(codec, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + STA32X_CONFF_PWDN); + msleep(300); + + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static struct snd_soc_dai_ops sta32x_dai_ops = { + .hw_params = sta32x_hw_params, + .set_sysclk = sta32x_set_dai_sysclk, + .set_fmt = sta32x_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sta32x_dai = { + .name = "STA32X", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA32X_RATES, + .formats = STA32X_FORMATS, + }, + .ops = &sta32x_dai_ops, +}; + +#ifdef CONFIG_PM +static int sta32x_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int sta32x_resume(struct snd_soc_codec *codec) +{ + sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define sta32x_suspend NULL +#define sta32x_resume NULL +#endif + +static int sta32x_probe(struct snd_soc_codec *codec) +{ + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + int i, ret = 0; + + sta32x->codec = codec; + + /* regulators */ + for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) + sta32x->supplies[i].supply = sta32x_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + /* Tell ASoC what kind of I/O to use to read the registers. ASoC will + * then do the I2C transactions itself. + */ + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + if (ret < 0) { + dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret); + return ret; + } + + /* read reg reset values into cache */ + for (i = 0; i < STA32X_REGISTER_COUNT; i++) + snd_soc_cache_write(codec, i, sta32x_regs[i]); + + /* preserve reset values of reserved register bits */ + snd_soc_cache_write(codec, STA32X_CONFC, + codec->hw_read(codec, STA32X_CONFC)); + snd_soc_cache_write(codec, STA32X_CONFE, + codec->hw_read(codec, STA32X_CONFE)); + snd_soc_cache_write(codec, STA32X_CONFF, + codec->hw_read(codec, STA32X_CONFF)); + snd_soc_cache_write(codec, STA32X_MMUTE, + codec->hw_read(codec, STA32X_MMUTE)); + snd_soc_cache_write(codec, STA32X_AUTO1, + codec->hw_read(codec, STA32X_AUTO1)); + snd_soc_cache_write(codec, STA32X_AUTO3, + codec->hw_read(codec, STA32X_AUTO3)); + snd_soc_cache_write(codec, STA32X_C3CFG, + codec->hw_read(codec, STA32X_C3CFG)); + + /* FIXME enable thermal warning adjustment and recovery */ + snd_soc_update_bits(codec, STA32X_CONFA, + STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, 0); + + /* FIXME select 2.1 mode */ + snd_soc_update_bits(codec, STA32X_CONFF, + STA32X_CONFF_OCFG_MASK, + 1 << STA32X_CONFF_OCFG_SHIFT); + + /* FIXME channel to output mapping */ + snd_soc_update_bits(codec, STA32X_C1CFG, + STA32X_CxCFG_OM_MASK, + 0 << STA32X_CxCFG_OM_SHIFT); + snd_soc_update_bits(codec, STA32X_C2CFG, + STA32X_CxCFG_OM_MASK, + 1 << STA32X_CxCFG_OM_SHIFT); + snd_soc_update_bits(codec, STA32X_C3CFG, + STA32X_CxCFG_OM_MASK, + 2 << STA32X_CxCFG_OM_SHIFT); + + sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + return 0; + +err_get: + regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); +err: + return ret; +} + +static int sta32x_remove(struct snd_soc_codec *codec) +{ + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + return 0; +} + +static int sta32x_reg_is_volatile(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case STA32X_CONFA ... STA32X_L2ATRT: + case STA32X_MPCC1 ... STA32X_FDRC2: + return 0; + } + return 1; +} + +static const struct snd_soc_codec_driver sta32x_codec = { + .probe = sta32x_probe, + .remove = sta32x_remove, + .suspend = sta32x_suspend, + .resume = sta32x_resume, + .reg_cache_size = STA32X_REGISTER_COUNT, + .reg_word_size = sizeof(u8), + .volatile_register = sta32x_reg_is_volatile, + .set_bias_level = sta32x_set_bias_level, + .controls = sta32x_snd_controls, + .num_controls = ARRAY_SIZE(sta32x_snd_controls), + .dapm_widgets = sta32x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets), + .dapm_routes = sta32x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes), +}; + +static __devinit int sta32x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sta32x_priv *sta32x; + int ret; + + sta32x = kzalloc(sizeof(struct sta32x_priv), GFP_KERNEL); + if (!sta32x) + return -ENOMEM; + + i2c_set_clientdata(i2c, sta32x); + + ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret); + return ret; + } + + return 0; +} + +static __devexit int sta32x_i2c_remove(struct i2c_client *client) +{ + struct sta32x_priv *sta32x = i2c_get_clientdata(client); + struct snd_soc_codec *codec = sta32x->codec; + + if (codec) + sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); + + regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + if (codec) { + snd_soc_unregister_codec(&client->dev); + snd_soc_codec_set_drvdata(codec, NULL); + } + + kfree(sta32x); + return 0; +} + +static const struct i2c_device_id sta32x_i2c_id[] = { + { "sta326", 0 }, + { "sta328", 0 }, + { "sta329", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id); + +static struct i2c_driver sta32x_i2c_driver = { + .driver = { + .name = "sta32x", + .owner = THIS_MODULE, + }, + .probe = sta32x_i2c_probe, + .remove = __devexit_p(sta32x_i2c_remove), + .id_table = sta32x_i2c_id, +}; + +static int __init sta32x_init(void) +{ + return i2c_add_driver(&sta32x_i2c_driver); +} +module_init(sta32x_init); + +static void __exit sta32x_exit(void) +{ + i2c_del_driver(&sta32x_i2c_driver); +} +module_exit(sta32x_exit); + +MODULE_DESCRIPTION("ASoC STA32X driver"); +MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h new file mode 100644 index 000000000000..b97ee5a75667 --- /dev/null +++ b/sound/soc/codecs/sta32x.h @@ -0,0 +1,210 @@ +/* + * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach <js@sig21.net> + * + * based on code from: + * Wolfson Microelectronics PLC. + * Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef _ASOC_STA_32X_H +#define _ASOC_STA_32X_H + +/* STA326 register addresses */ + +#define STA32X_REGISTER_COUNT 0x2d + +#define STA32X_CONFA 0x00 +#define STA32X_CONFB 0x01 +#define STA32X_CONFC 0x02 +#define STA32X_CONFD 0x03 +#define STA32X_CONFE 0x04 +#define STA32X_CONFF 0x05 +#define STA32X_MMUTE 0x06 +#define STA32X_MVOL 0x07 +#define STA32X_C1VOL 0x08 +#define STA32X_C2VOL 0x09 +#define STA32X_C3VOL 0x0a +#define STA32X_AUTO1 0x0b +#define STA32X_AUTO2 0x0c +#define STA32X_AUTO3 0x0d +#define STA32X_C1CFG 0x0e +#define STA32X_C2CFG 0x0f +#define STA32X_C3CFG 0x10 +#define STA32X_TONE 0x11 +#define STA32X_L1AR 0x12 +#define STA32X_L1ATRT 0x13 +#define STA32X_L2AR 0x14 +#define STA32X_L2ATRT 0x15 +#define STA32X_CFADDR2 0x16 +#define STA32X_B1CF1 0x17 +#define STA32X_B1CF2 0x18 +#define STA32X_B1CF3 0x19 +#define STA32X_B2CF1 0x1a +#define STA32X_B2CF2 0x1b +#define STA32X_B2CF3 0x1c +#define STA32X_A1CF1 0x1d +#define STA32X_A1CF2 0x1e +#define STA32X_A1CF3 0x1f +#define STA32X_A2CF1 0x20 +#define STA32X_A2CF2 0x21 +#define STA32X_A2CF3 0x22 +#define STA32X_B0CF1 0x23 +#define STA32X_B0CF2 0x24 +#define STA32X_B0CF3 0x25 +#define STA32X_CFUD 0x26 +#define STA32X_MPCC1 0x27 +#define STA32X_MPCC2 0x28 +/* Reserved 0x29 */ +/* Reserved 0x2a */ +#define STA32X_Reserved 0x2a +#define STA32X_FDRC1 0x2b +#define STA32X_FDRC2 0x2c +/* Reserved 0x2d */ + + +/* STA326 register field definitions */ + +/* 0x00 CONFA */ +#define STA32X_CONFA_MCS_MASK 0x03 +#define STA32X_CONFA_MCS_SHIFT 0 +#define STA32X_CONFA_IR_MASK 0x18 +#define STA32X_CONFA_IR_SHIFT 3 +#define STA32X_CONFA_TWRB 0x20 +#define STA32X_CONFA_TWAB 0x40 +#define STA32X_CONFA_FDRB 0x80 + +/* 0x01 CONFB */ +#define STA32X_CONFB_SAI_MASK 0x0f +#define STA32X_CONFB_SAI_SHIFT 0 +#define STA32X_CONFB_SAIFB 0x10 +#define STA32X_CONFB_DSCKE 0x20 +#define STA32X_CONFB_C1IM 0x40 +#define STA32X_CONFB_C2IM 0x80 + +/* 0x02 CONFC */ +#define STA32X_CONFC_OM_MASK 0x03 +#define STA32X_CONFC_OM_SHIFT 0 +#define STA32X_CONFC_CSZ_MASK 0x7c +#define STA32X_CONFC_CSZ_SHIFT 2 + +/* 0x03 CONFD */ +#define STA32X_CONFD_HPB 0x01 +#define STA32X_CONFD_HPB_SHIFT 0 +#define STA32X_CONFD_DEMP 0x02 +#define STA32X_CONFD_DEMP_SHIFT 1 +#define STA32X_CONFD_DSPB 0x04 +#define STA32X_CONFD_DSPB_SHIFT 2 +#define STA32X_CONFD_PSL 0x08 +#define STA32X_CONFD_PSL_SHIFT 3 +#define STA32X_CONFD_BQL 0x10 +#define STA32X_CONFD_BQL_SHIFT 4 +#define STA32X_CONFD_DRC 0x20 +#define STA32X_CONFD_DRC_SHIFT 5 +#define STA32X_CONFD_ZDE 0x40 +#define STA32X_CONFD_ZDE_SHIFT 6 +#define STA32X_CONFD_MME 0x80 +#define STA32X_CONFD_MME_SHIFT 7 + +/* 0x04 CONFE */ +#define STA32X_CONFE_MPCV 0x01 +#define STA32X_CONFE_MPCV_SHIFT 0 +#define STA32X_CONFE_MPC 0x02 +#define STA32X_CONFE_MPC_SHIFT 1 +#define STA32X_CONFE_AME 0x08 +#define STA32X_CONFE_AME_SHIFT 3 +#define STA32X_CONFE_PWMS 0x10 +#define STA32X_CONFE_PWMS_SHIFT 4 +#define STA32X_CONFE_ZCE 0x40 +#define STA32X_CONFE_ZCE_SHIFT 6 +#define STA32X_CONFE_SVE 0x80 +#define STA32X_CONFE_SVE_SHIFT 7 + +/* 0x05 CONFF */ +#define STA32X_CONFF_OCFG_MASK 0x03 +#define STA32X_CONFF_OCFG_SHIFT 0 +#define STA32X_CONFF_IDE 0x04 +#define STA32X_CONFF_IDE_SHIFT 3 +#define STA32X_CONFF_BCLE 0x08 +#define STA32X_CONFF_ECLE 0x20 +#define STA32X_CONFF_PWDN 0x40 +#define STA32X_CONFF_EAPD 0x80 + +/* 0x06 MMUTE */ +#define STA32X_MMUTE_MMUTE 0x01 + +/* 0x0b AUTO1 */ +#define STA32X_AUTO1_AMEQ_MASK 0x03 +#define STA32X_AUTO1_AMEQ_SHIFT 0 +#define STA32X_AUTO1_AMV_MASK 0xc0 +#define STA32X_AUTO1_AMV_SHIFT 2 +#define STA32X_AUTO1_AMGC_MASK 0x30 +#define STA32X_AUTO1_AMGC_SHIFT 4 +#define STA32X_AUTO1_AMPS 0x80 + +/* 0x0c AUTO2 */ +#define STA32X_AUTO2_AMAME 0x01 +#define STA32X_AUTO2_AMAM_MASK 0x0e +#define STA32X_AUTO2_AMAM_SHIFT 1 +#define STA32X_AUTO2_XO_MASK 0xf0 +#define STA32X_AUTO2_XO_SHIFT 4 + +/* 0x0d AUTO3 */ +#define STA32X_AUTO3_PEQ_MASK 0x1f +#define STA32X_AUTO3_PEQ_SHIFT 0 + +/* 0x0e 0x0f 0x10 CxCFG */ +#define STA32X_CxCFG_TCB 0x01 /* only C1 and C2 */ +#define STA32X_CxCFG_TCB_SHIFT 0 +#define STA32X_CxCFG_EQBP 0x02 /* only C1 and C2 */ +#define STA32X_CxCFG_EQBP_SHIFT 1 +#define STA32X_CxCFG_VBP 0x03 +#define STA32X_CxCFG_VBP_SHIFT 2 +#define STA32X_CxCFG_BO 0x04 +#define STA32X_CxCFG_LS_MASK 0x30 +#define STA32X_CxCFG_LS_SHIFT 4 +#define STA32X_CxCFG_OM_MASK 0xc0 +#define STA32X_CxCFG_OM_SHIFT 6 + +/* 0x11 TONE */ +#define STA32X_TONE_BTC_SHIFT 0 +#define STA32X_TONE_TTC_SHIFT 4 + +/* 0x12 0x13 0x14 0x15 limiter attack/release */ +#define STA32X_LxA_SHIFT 0 +#define STA32X_LxR_SHIFT 4 + +/* 0x26 CFUD */ +#define STA32X_CFUD_W1 0x01 +#define STA32X_CFUD_WA 0x02 +#define STA32X_CFUD_R1 0x04 +#define STA32X_CFUD_RA 0x08 + + +/* biquad filter coefficient table offsets */ +#define STA32X_C1_BQ_BASE 0 +#define STA32X_C2_BQ_BASE 20 +#define STA32X_CH_BQ_NUM 4 +#define STA32X_BQ_NUM_COEF 5 +#define STA32X_XO_HP_BQ_BASE 40 +#define STA32X_XO_LP_BQ_BASE 45 +#define STA32X_C1_PRESCALE 50 +#define STA32X_C2_PRESCALE 51 +#define STA32X_C1_POSTSCALE 52 +#define STA32X_C2_POSTSCALE 53 +#define STA32X_C3_POSTSCALE 54 +#define STA32X_TW_POSTSCALE 55 +#define STA32X_C1_MIX1 56 +#define STA32X_C1_MIX2 57 +#define STA32X_C2_MIX1 58 +#define STA32X_C2_MIX2 59 +#define STA32X_C3_MIX1 60 +#define STA32X_C3_MIX2 61 + +#endif /* _ASOC_STA_32X_H */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 789453d44ec5..0963c4c7a83f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -226,11 +226,13 @@ static const char *aic3x_adc_hpf[] = #define RDAC_ENUM 1 #define LHPCOM_ENUM 2 #define RHPCOM_ENUM 3 -#define LINE1L_ENUM 4 -#define LINE1R_ENUM 5 -#define LINE2L_ENUM 6 -#define LINE2R_ENUM 7 -#define ADC_HPF_ENUM 8 +#define LINE1L_2_L_ENUM 4 +#define LINE1L_2_R_ENUM 5 +#define LINE1R_2_L_ENUM 6 +#define LINE1R_2_R_ENUM 7 +#define LINE2L_ENUM 8 +#define LINE2R_ENUM 9 +#define ADC_HPF_ENUM 10 static const struct soc_enum aic3x_enum[] = { SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), @@ -238,6 +240,8 @@ static const struct soc_enum aic3x_enum[] = { SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux), SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux), SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), + SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), + SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), @@ -490,12 +494,16 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { }; /* Left Line1 Mux */ -static const struct snd_kcontrol_new aic3x_left_line1_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]); +static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]); +static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]); /* Right Line1 Mux */ -static const struct snd_kcontrol_new aic3x_right_line1_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]); +static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]); +static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]); /* Left Line2 Mux */ static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = @@ -535,9 +543,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { &aic3x_left_pga_mixer_controls[0], ARRAY_SIZE(aic3x_left_pga_mixer_controls)), SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, - &aic3x_left_line1_mux_controls), + &aic3x_left_line1l_mux_controls), SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0, - &aic3x_left_line1_mux_controls), + &aic3x_left_line1r_mux_controls), SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, &aic3x_left_line2_mux_controls), @@ -548,9 +556,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { &aic3x_right_pga_mixer_controls[0], ARRAY_SIZE(aic3x_right_pga_mixer_controls)), SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0, - &aic3x_right_line1_mux_controls), + &aic3x_right_line1l_mux_controls), SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, - &aic3x_right_line1_mux_controls), + &aic3x_right_line1r_mux_controls), SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0, &aic3x_right_line2_mux_controls), diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index bec788b12613..71674bec9604 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -36,7 +36,7 @@ #include <sound/tlv.h> /* Register descriptions are here */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> /* Shadow register used by the audio driver */ #define TWL4030_REG_SW_SHADOW 0x4A @@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) return; if (enable) - mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); else - mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); if (mode >= 0) { twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); @@ -297,7 +297,7 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec) static void twl4030_init_chip(struct snd_soc_codec *codec) { - struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 reg, byte; int i = 0; @@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) if (enable) { twl4030->apll_enabled++; if (twl4030->apll_enabled == 1) - status = twl4030_codec_enable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_enable_resource( + TWL4030_AUDIO_RES_APLL); } else { twl4030->apll_enabled--; if (!twl4030->apll_enabled) - status = twl4030_codec_disable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_disable_resource( + TWL4030_AUDIO_RES_APLL); } if (status >= 0) @@ -732,7 +732,7 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_data *pdata = codec->dev->platform_data; unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2260,7 +2260,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) } snd_soc_codec_set_drvdata(codec, twl4030); /* Set the defaults, and power up the codec */ - twl4030->sysclk = twl4030_codec_get_mclk() / 1000; + twl4030->sysclk = twl4030_audio_get_mclk() / 1000; codec->dapm.idle_bias_off = 1; twl4030_init_chip(codec); @@ -2297,7 +2297,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 4c336636d4f5..443032b3b329 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -24,11 +24,10 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/i2c/twl.h> +#include <linux/mfd/twl6040.h> #include <sound/core.h> #include <sound/pcm.h> @@ -77,14 +76,19 @@ struct twl6040_jack_data { /* codec private data */ struct twl6040_data { - int audpwron; - int naudint; + int plug_irq; int codec_powered; int pll; int non_lp; + int pll_power_mode; + int hs_power_mode; + int hs_power_mode_locked; + unsigned int clk_in; unsigned int sysclk; - struct snd_pcm_hw_constraint_list *sysclk_constraints; - struct completion ready; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct workqueue_struct *workqueue; @@ -206,6 +210,32 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { TWL6040_REG_DLB, }; +/* set of rates for each pll: low-power and high-performance */ +static unsigned int lp_rates[] = { + 8000, + 11250, + 16000, + 22500, + 32000, + 44100, + 48000, + 88200, + 96000, +}; + +static unsigned int hp_rates[] = { + 8000, + 16000, + 32000, + 48000, + 96000, +}; + +static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { + { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, + { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, +}; + /* * read twl6040 register cache */ @@ -239,12 +269,13 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, unsigned int reg) { + struct twl6040 *twl6040 = codec->control_data; u8 value; if (reg >= TWL6040_CACHEREGNUM) return -EIO; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); + value = twl6040_reg_read(twl6040, reg); twl6040_write_reg_cache(codec, reg, value); return value; @@ -256,11 +287,13 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + struct twl6040 *twl6040 = codec->control_data; + if (reg >= TWL6040_CACHEREGNUM) return -EIO; twl6040_write_reg_cache(codec, reg, value); - return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); + return twl6040_reg_write(twl6040, reg, value); } static void twl6040_init_vio_regs(struct snd_soc_codec *codec) @@ -268,15 +301,21 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec) u8 *cache = codec->reg_cache; int reg, i; - /* allow registers to be accessed by i2c */ - twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); - for (i = 0; i < TWL6040_VIOREGNUM; i++) { reg = twl6040_vio_reg[i]; - /* skip read-only registers (ASICID, ASICREV, STATUS) */ + /* + * skip read-only registers (ASICID, ASICREV, STATUS) + * and registers shared among MFD children + */ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: + case TWL6040_REG_INTID: + case TWL6040_REG_INTMR: + case TWL6040_REG_NCPCTL: + case TWL6040_REG_LDOCTL: + case TWL6040_REG_GPOCTL: + case TWL6040_REG_ACCCTL: case TWL6040_REG_STATUS: continue; default: @@ -293,6 +332,20 @@ static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) for (i = 0; i < TWL6040_VDDREGNUM; i++) { reg = twl6040_vdd_reg[i]; + /* skip vibra and PLL registers */ + switch (reg) { + case TWL6040_REG_VIBCTLL: + case TWL6040_REG_VIBDATL: + case TWL6040_REG_VIBCTLR: + case TWL6040_REG_VIBDATR: + case TWL6040_REG_HPPLLCTL: + case TWL6040_REG_LPPLLCTL: + case TWL6040_REG_LPPLLDIV: + continue; + default: + break; + } + twl6040_write(codec, reg, cache[reg]); } } @@ -317,7 +370,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->left_vol) { - val += left_step; + if (val + left_step > headset->left_vol) + val = headset->left_vol; + else + val += left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val & TWL6040_HSL_VOL_MASK))); @@ -327,7 +384,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val & TWL6040_HSL_VOL_MASK)); @@ -344,7 +405,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->right_vol) { - val += right_step; + if (val + right_step > headset->right_vol) + val = headset->right_vol; + else + val += right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val << TWL6040_HSR_VOL_SHIFT))); @@ -354,7 +419,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val << TWL6040_HSR_VOL_SHIFT)); @@ -385,7 +454,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->left_vol) { - val += left_step; + if (val + left_step > handsfree->left_vol) + val = handsfree->left_vol; + else + val += left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -395,7 +468,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -412,7 +489,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->right_vol) { - val += right_step; + if (val + right_step > handsfree->right_vol) + val = handsfree->right_vol; + else + val += right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -422,7 +503,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -451,11 +536,9 @@ static void twl6040_pga_hs_work(struct work_struct *work) /* HS PGA volumes have 4 bits of resolution to ramp */ for (i = 0; i <= 16; i++) { - headset_complete = 1; - if (headset->ramp != TWL6040_RAMP_NONE) - headset_complete = twl6040_hs_ramp_step(codec, - headset->left_step, - headset->right_step); + headset_complete = twl6040_hs_ramp_step(codec, + headset->left_step, + headset->right_step); /* ramp finished ? */ if (headset_complete) @@ -496,11 +579,9 @@ static void twl6040_pga_hf_work(struct work_struct *work) /* HF PGA volumes have 5 bits of resolution to ramp */ for (i = 0; i <= 32; i++) { - handsfree_complete = 1; - if (handsfree->ramp != TWL6040_RAMP_NONE) - handsfree_complete = twl6040_hf_ramp_step(codec, - handsfree->left_step, - handsfree->right_step); + handsfree_complete = twl6040_hf_ramp_step(codec, + handsfree->left_step, + handsfree->right_step); /* ramp finished ? */ if (handsfree_complete) @@ -541,12 +622,16 @@ static int pga_event(struct snd_soc_dapm_widget *w, out = &priv->headset; work = &priv->hs_delayed_work; queue = priv->hs_workqueue; + out->left_step = priv->hs_left_step; + out->right_step = priv->hs_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ break; case 4: out = &priv->handsfree; work = &priv->hf_delayed_work; queue = priv->hf_workqueue; + out->left_step = priv->hf_left_step; + out->right_step = priv->hf_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ if (SND_SOC_DAPM_EVENT_ON(event)) priv->non_lp++; @@ -579,8 +664,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, if (!delayed_work_pending(work)) { /* use volume ramp for power-down */ - out->left_step = 1; - out->right_step = 1; out->ramp = TWL6040_RAMP_DOWN; INIT_COMPLETION(out->ramp_done); @@ -596,88 +679,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, return 0; } -/* twl6040 codec manual power-up sequence */ -static void twl6040_power_up(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable reference system */ - ldoctl |= TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* enable high-side ldo */ - ldoctl |= TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable negative charge pump */ - ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* enable low-side ldo */ - ldoctl |= TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* reset state machine */ - accctl |= TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - mdelay(5); - accctl &= ~TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); -} - -/* twl6040 codec manual power-down sequence */ -static void twl6040_power_down(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* disable low-power pll */ - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* disable low-side ldo */ - ldoctl &= ~TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable negative charge pump */ - ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN); - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* disable high-side ldo */ - ldoctl &= ~TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - /* disable reference system */ - ldoctl &= ~TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); -} - /* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { @@ -713,15 +714,26 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; - if (SND_SOC_DAPM_EVENT_ON(event)) + if (SND_SOC_DAPM_EVENT_ON(event)) { priv->non_lp++; - else + if (!strcmp(w->name, "Earphone Driver")) { + /* Earphone doesn't support low power mode */ + priv->hs_power_mode_locked = 1; + ret = headset_power_mode(codec, 1); + } + } else { priv->non_lp--; + if (!strcmp(w->name, "Earphone Driver")) { + priv->hs_power_mode_locked = 0; + ret = headset_power_mode(codec, priv->hs_power_mode); + } + } msleep(1); - return 0; + return ret; } static void twl6040_hs_jack_report(struct snd_soc_codec *codec, @@ -766,33 +778,19 @@ static void twl6040_accessory_work(struct work_struct *work) } /* audio interrupt handler */ -static irqreturn_t twl6040_naudint_handler(int irq, void *data) +static irqreturn_t twl6040_audio_handler(int irq, void *data) { struct snd_soc_codec *codec = data; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 intid; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); - - if (intid & TWL6040_THINT) - dev_alert(codec->dev, "die temp over-limit detection\n"); + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) queue_delayed_work(priv->workqueue, &priv->delayed_work, msecs_to_jiffies(200)); - if (intid & TWL6040_HOOKINT) - dev_info(codec->dev, "hook detection\n"); - - if (intid & TWL6040_HFINT) - dev_alert(codec->dev, "hf drivers over current detection\n"); - - if (intid & TWL6040_VIBINT) - dev_alert(codec->dev, "vib drivers over current detection\n"); - - if (intid & TWL6040_READYINT) - complete(&priv->ready); - return IRQ_HANDLED; } @@ -954,9 +952,9 @@ static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); /* * MICGAIN volume control: - * from -6 to 30 dB in 6 dB steps + * from 6 to 30 dB in 6 dB steps */ -static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0); +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0); /* * AFMGAIN volume control: @@ -1040,6 +1038,73 @@ static const struct snd_kcontrol_new hfr_mux_controls = static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); +/* Headset power mode */ +static const char *twl6040_power_mode_texts[] = { + "Low-Power", "High-Perfomance", +}; + +static const struct soc_enum twl6040_power_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts), + twl6040_power_mode_texts); + +static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->hs_power_mode; + + return 0; +} + +static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int high_perf = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (!priv->hs_power_mode_locked) + ret = headset_power_mode(codec, high_perf); + + if (!ret) + priv->hs_power_mode = high_perf; + + return ret; +} + +static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->pll_power_mode; + + return 0; +} + +static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + priv->pll_power_mode = ucontrol->value.enumerated.item[0]; + + return 0; +} + +int twl6040_get_clk_id(struct snd_soc_codec *codec) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + return priv->pll_power_mode; +} +EXPORT_SYMBOL_GPL(twl6040_get_clk_id); + static const struct snd_kcontrol_new twl6040_snd_controls[] = { /* Capture gains */ SOC_DOUBLE_TLV("Capture Preamplifier Volume", @@ -1058,6 +1123,13 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), + + SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, + twl6040_headset_power_get_enum, + twl6040_headset_power_put_enum), + + SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum, + twl6040_pll_get_enum, twl6040_pll_put_enum), }; static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { @@ -1231,36 +1303,11 @@ static int twl6040_add_widgets(struct snd_soc_codec *codec) return 0; } -static int twl6040_power_up_completion(struct snd_soc_codec *codec, - int naudint) -{ - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int time_left; - u8 intid; - - time_left = wait_for_completion_timeout(&priv->ready, - msecs_to_jiffies(144)); - - if (!time_left) { - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, - TWL6040_REG_INTID); - if (!(intid & TWL6040_READYINT)) { - dev_err(codec->dev, "timeout waiting for READYINT\n"); - return -ETIMEDOUT; - } - } - - priv->codec_powered = 1; - - return 0; -} - static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; int ret; switch (level) { @@ -1272,58 +1319,23 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, if (priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 1); + ret = twl6040_power(twl6040, 1); + if (ret) + return ret; - /* wait for power-up completion */ - ret = twl6040_power_up_completion(codec, naudint); - if (ret) - return ret; - - /* sync registers updated during power-up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); - } else { - /* use manual power-up sequence */ - twl6040_power_up(codec); - priv->codec_powered = 1; - } + priv->codec_powered = 1; /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); /* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); - - /* Set initial minimal gain values */ - twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); - twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); - twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); - twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 0); - - /* power-down sequence latency */ - udelay(500); - - /* sync registers updated during power-down sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, - 0x00); - } else { - /* use manual power-down sequence */ - twl6040_power_down(codec); - } - + twl6040_power(twl6040, 0); priv->codec_powered = 0; break; } @@ -1333,27 +1345,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, return 0; } -/* set of rates for each pll: low-power and high-performance */ - -static unsigned int lp_rates[] = { - 88200, - 96000, -}; - -static struct snd_pcm_hw_constraint_list lp_constraints = { - .count = ARRAY_SIZE(lp_rates), - .list = lp_rates, -}; - -static unsigned int hp_rates[] = { - 96000, -}; - -static struct snd_pcm_hw_constraint_list hp_constraints = { - .count = ARRAY_SIZE(hp_rates), - .list = hp_rates, -}; - static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1363,7 +1354,7 @@ static int twl6040_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - priv->sysclk_constraints); + &sysclk_constraints[priv->pll_power_mode]); return 0; } @@ -1375,22 +1366,27 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 lppllctl; int rate; - /* nothing to do for high-perf pll, it supports only 48 kHz */ - if (priv->pll == TWL6040_HPPLL_ID) - return 0; - - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - rate = params_rate(params); switch (rate) { case 11250: case 22500: case 44100: case 88200: - lppllctl |= TWL6040_LPLLFIN; + /* These rates are not supported when HPPLL is in use */ + if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) { + dev_err(codec->dev, "HPPLL does not support rate %d\n", + rate); + return -EINVAL; + } + /* Capture is not supported with 17.64MHz sysclk */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(codec->dev, + "capture mode is not supported at %dHz\n", + rate); + return -EINVAL; + } priv->sysclk = 17640000; break; case 8000: @@ -1398,7 +1394,6 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, case 32000: case 48000: case 96000: - lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; break; default: @@ -1406,8 +1401,6 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - return 0; } @@ -1416,7 +1409,9 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret; if (!priv->sysclk) { dev_err(codec->dev, @@ -1424,24 +1419,19 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, return -EINVAL; } - /* - * capture is not supported at 17.64 MHz, - * it's reserved for headset low-power playback scenario - */ - if ((priv->sysclk == 17640000) && - substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - dev_err(codec->dev, - "capture mode is not supported at %dHz\n", - priv->sysclk); - return -EINVAL; - } - if ((priv->sysclk == 17640000) && priv->non_lp) { dev_err(codec->dev, "some enabled paths aren't supported at %dHz\n", priv->sysclk); return -EPERM; } + + ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); + if (ret) { + dev_err(codec->dev, "Can not set PLL (%d)\n", ret); + return -EPERM; + } + return 0; } @@ -1450,99 +1440,12 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 hppllctl, lppllctl; - - hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: - switch (freq) { - case 32768: - /* headset dac and driver must be in low-power mode */ - headset_power_mode(codec, 0); - - /* clk32k input requires low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - mdelay(5); - lppllctl &= ~TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - hppllctl &= ~TWL6040_HPLLENA; - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } - - /* lppll divider */ - switch (priv->sysclk) { - case 17640000: - lppllctl |= TWL6040_LPLLFIN; - break; - case 19200000: - lppllctl &= ~TWL6040_LPLLFIN; - break; - default: - /* sysclk not yet configured */ - lppllctl &= ~TWL6040_LPLLFIN; - priv->sysclk = 19200000; - break; - } - - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - - priv->pll = TWL6040_LPPLL_ID; - priv->sysclk_constraints = &lp_constraints; - break; case TWL6040_SYSCLK_SEL_HPPLL: - hppllctl &= ~TWL6040_MCLK_MSK; - - switch (freq) { - case 12000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_12000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 19200000: - /* mclk input, pll disabled */ - hppllctl |= TWL6040_MCLK_19200KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - case 26000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_26000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 38400000: - /* clk slicer, pll disabled */ - hppllctl |= TWL6040_MCLK_38400KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } - - /* headset dac and driver must be in high-performance mode */ - headset_power_mode(codec, 1); - - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - udelay(500); - lppllctl |= TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - - /* high-performance pll can provide only 19.2 MHz */ - priv->pll = TWL6040_HPPLL_ID; - priv->sysclk = 19200000; - priv->sysclk_constraints = &hp_constraints; + priv->pll = clk_id; + priv->clk_in = freq; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); @@ -1559,15 +1462,27 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { .set_sysclk = twl6040_set_dai_sysclk, }; -static struct snd_soc_dai_driver twl6040_dai = { +static struct snd_soc_dai_driver twl6040_dai[] = { +{ .name = "twl6040-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 4, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-ul", .capture = { .stream_name = "Capture", .channels_min = 1, @@ -1576,6 +1491,40 @@ static struct snd_soc_dai_driver twl6040_dai = { .formats = TWL6040_FORMATS, }, .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl1", + .playback = { + .stream_name = "Headset Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl2", + .playback = { + .stream_name = "Handsfree Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-vib", + .playback = { + .stream_name = "Vibra Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, }; #ifdef CONFIG_PM @@ -1600,11 +1549,11 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { - struct twl4030_codec_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; - int audpwron, naudint; + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); + struct platform_device *pdev = container_of(codec->dev, + struct platform_device, dev); int ret = 0; - u8 icrev, intmr = TWL6040_ALLINT_MSK; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) @@ -1612,23 +1561,32 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; + codec->control_data = dev_get_drvdata(codec->dev->parent); - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); + if (pdata && pdata->hs_left_step && pdata->hs_right_step) { + priv->hs_left_step = pdata->hs_left_step; + priv->hs_right_step = pdata->hs_right_step; + } else { + priv->hs_left_step = 1; + priv->hs_right_step = 1; + } - if (twl_codec && (icrev > 0)) - audpwron = twl_codec->audpwron_gpio; - else - audpwron = -EINVAL; + if (pdata && pdata->hf_left_step && pdata->hf_right_step) { + priv->hf_left_step = pdata->hf_left_step; + priv->hf_right_step = pdata->hf_right_step; + } else { + priv->hf_left_step = 1; + priv->hf_right_step = 1; + } - if (twl_codec) - naudint = twl_codec->naudint_irq; - else - naudint = 0; + priv->plug_irq = platform_get_irq(pdev, 0); + if (priv->plug_irq < 0) { + dev_err(codec->dev, "invalid irq\n"); + ret = -EINVAL; + goto work_err; + } - priv->audpwron = audpwron; - priv->naudint = naudint; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); - if (!priv->workqueue) { ret = -ENOMEM; goto work_err; @@ -1638,56 +1596,33 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); - init_completion(&priv->ready); init_completion(&priv->headset.ramp_done); init_completion(&priv->handsfree.ramp_done); - if (gpio_is_valid(audpwron)) { - ret = gpio_request(audpwron, "audpwron"); - if (ret) - goto gpio1_err; - - ret = gpio_direction_output(audpwron, 0); - if (ret) - goto gpio2_err; - - priv->codec_powered = 0; - - /* enable only codec ready interrupt */ - intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); - - /* reset interrupt status to allow correct power up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); - } - twl6040_write(codec, TWL6040_REG_INTMR, intmr); - - if (naudint) { - /* audio interrupt */ - ret = request_threaded_irq(naudint, NULL, - twl6040_naudint_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "twl6040_codec", codec); - if (ret) - goto gpio2_err; - } - - /* init vio registers */ - twl6040_init_vio_regs(codec); - priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); if (priv->hf_workqueue == NULL) { ret = -ENOMEM; - goto irq_err; + goto hfwq_err; } priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); if (priv->hs_workqueue == NULL) { ret = -ENOMEM; - goto wq_err; + goto hswq_err; } INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); + ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, + 0, "twl6040_irq_plug", codec); + if (ret) { + dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); + goto plugirq_err; + } + + /* init vio registers */ + twl6040_init_vio_regs(codec); + /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) @@ -1700,16 +1635,12 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; bias_err: + free_irq(priv->plug_irq, codec); +plugirq_err: destroy_workqueue(priv->hs_workqueue); -wq_err: +hswq_err: destroy_workqueue(priv->hf_workqueue); -irq_err: - if (naudint) - free_irq(naudint, codec); -gpio2_err: - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); -gpio1_err: +hfwq_err: destroy_workqueue(priv->workqueue); work_err: kfree(priv); @@ -1719,17 +1650,9 @@ work_err: static int twl6040_remove(struct snd_soc_codec *codec) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); - - if (naudint) - free_irq(naudint, codec); - + free_irq(priv->plug_irq, codec); destroy_workqueue(priv->workqueue); destroy_workqueue(priv->hf_workqueue); destroy_workqueue(priv->hs_workqueue); @@ -1753,8 +1676,8 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { static int __devinit twl6040_codec_probe(struct platform_device *pdev) { - return snd_soc_register_codec(&pdev->dev, - &soc_codec_dev_twl6040, &twl6040_dai, 1); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040, + twl6040_dai, ARRAY_SIZE(twl6040_dai)); } static int __devexit twl6040_codec_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index 23aeed0963e6..d8de67869dd9 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -22,125 +22,8 @@ #ifndef __TWL6040_H__ #define __TWL6040_H__ -#define TWL6040_REG_ASICID 0x01 -#define TWL6040_REG_ASICREV 0x02 -#define TWL6040_REG_INTID 0x03 -#define TWL6040_REG_INTMR 0x04 -#define TWL6040_REG_NCPCTL 0x05 -#define TWL6040_REG_LDOCTL 0x06 -#define TWL6040_REG_HPPLLCTL 0x07 -#define TWL6040_REG_LPPLLCTL 0x08 -#define TWL6040_REG_LPPLLDIV 0x09 -#define TWL6040_REG_AMICBCTL 0x0A -#define TWL6040_REG_DMICBCTL 0x0B -#define TWL6040_REG_MICLCTL 0x0C -#define TWL6040_REG_MICRCTL 0x0D -#define TWL6040_REG_MICGAIN 0x0E -#define TWL6040_REG_LINEGAIN 0x0F -#define TWL6040_REG_HSLCTL 0x10 -#define TWL6040_REG_HSRCTL 0x11 -#define TWL6040_REG_HSGAIN 0x12 -#define TWL6040_REG_EARCTL 0x13 -#define TWL6040_REG_HFLCTL 0x14 -#define TWL6040_REG_HFLGAIN 0x15 -#define TWL6040_REG_HFRCTL 0x16 -#define TWL6040_REG_HFRGAIN 0x17 -#define TWL6040_REG_VIBCTLL 0x18 -#define TWL6040_REG_VIBDATL 0x19 -#define TWL6040_REG_VIBCTLR 0x1A -#define TWL6040_REG_VIBDATR 0x1B -#define TWL6040_REG_HKCTL1 0x1C -#define TWL6040_REG_HKCTL2 0x1D -#define TWL6040_REG_GPOCTL 0x1E -#define TWL6040_REG_ALB 0x1F -#define TWL6040_REG_DLB 0x20 -#define TWL6040_REG_TRIM1 0x28 -#define TWL6040_REG_TRIM2 0x29 -#define TWL6040_REG_TRIM3 0x2A -#define TWL6040_REG_HSOTRIM 0x2B -#define TWL6040_REG_HFOTRIM 0x2C -#define TWL6040_REG_ACCCTL 0x2D -#define TWL6040_REG_STATUS 0x2E - -#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) - -#define TWL6040_VIOREGNUM 18 -#define TWL6040_VDDREGNUM 21 - -/* INTID (0x03) fields */ - -#define TWL6040_THINT 0x01 -#define TWL6040_PLUGINT 0x02 -#define TWL6040_UNPLUGINT 0x04 -#define TWL6040_HOOKINT 0x08 -#define TWL6040_HFINT 0x10 -#define TWL6040_VIBINT 0x20 -#define TWL6040_READYINT 0x40 - -/* INTMR (0x04) fields */ - -#define TWL6040_PLUGMSK 0x02 -#define TWL6040_READYMSK 0x40 -#define TWL6040_ALLINT_MSK 0x7B - -/* NCPCTL (0x05) fields */ - -#define TWL6040_NCPENA 0x01 -#define TWL6040_NCPOPEN 0x40 - -/* LDOCTL (0x06) fields */ - -#define TWL6040_LSLDOENA 0x01 -#define TWL6040_HSLDOENA 0x04 -#define TWL6040_REFENA 0x40 -#define TWL6040_OSCENA 0x80 - -/* HPPLLCTL (0x07) fields */ - -#define TWL6040_HPLLENA 0x01 -#define TWL6040_HPLLRST 0x02 -#define TWL6040_HPLLBP 0x04 -#define TWL6040_HPLLSQRENA 0x08 -#define TWL6040_HPLLSQRBP 0x10 -#define TWL6040_MCLK_12000KHZ (0 << 5) -#define TWL6040_MCLK_19200KHZ (1 << 5) -#define TWL6040_MCLK_26000KHZ (2 << 5) -#define TWL6040_MCLK_38400KHZ (3 << 5) -#define TWL6040_MCLK_MSK 0x60 - -/* LPPLLCTL (0x08) fields */ - -#define TWL6040_LPLLENA 0x01 -#define TWL6040_LPLLRST 0x02 -#define TWL6040_LPLLSEL 0x04 -#define TWL6040_LPLLFIN 0x08 -#define TWL6040_HPLLSEL 0x10 - -/* HSLCTL (0x10) fields */ - -#define TWL6040_HSDACMODEL 0x02 -#define TWL6040_HSDRVMODEL 0x08 - -/* HSRCTL (0x11) fields */ - -#define TWL6040_HSDACMODER 0x02 -#define TWL6040_HSDRVMODER 0x08 - -/* ACCCTL (0x2D) fields */ - -#define TWL6040_RESETSPLIT 0x04 - -#define TWL6040_SYSCLK_SEL_LPPLL 1 -#define TWL6040_SYSCLK_SEL_HPPLL 2 - -#define TWL6040_HPPLL_ID 1 -#define TWL6040_LPPLL_ID 2 - -/* STATUS (0x2E) fields */ - -#define TWL6040_PLUGCOMP 0x02 - void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report); +int twl6040_get_clk_id(struct snd_soc_codec *codec); #endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c new file mode 100644 index 000000000000..a2a09f85ea99 --- /dev/null +++ b/sound/soc/codecs/wm8782.c @@ -0,0 +1,80 @@ +/* + * sound/soc/codecs/wm8782.c + * simple, strap-pin configured 24bit 2ch ADC + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach <js@sig21.net> + * + * based on ad73311.c + * Copyright: Analog Device Inc. + * Author: Cliff Cai <cliff.cai@analog.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. + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> + +static struct snd_soc_dai_driver wm8782_dai = { + .name = "wm8782", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + /* For configurations with FSAMPEN=0 */ + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8782; + +static __devinit int wm8782_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_wm8782, &wm8782_dai, 1); +} + +static int __devexit wm8782_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver wm8782_codec_driver = { + .driver = { + .name = "wm8782", + .owner = THIS_MODULE, + }, + .probe = wm8782_probe, + .remove = wm8782_remove, +}; + +static int __init wm8782_init(void) +{ + return platform_driver_register(&wm8782_codec_driver); +} +module_init(wm8782_init); + +static void __exit wm8782_exit(void) +{ + platform_driver_unregister(&wm8782_codec_driver); +} +module_exit(wm8782_exit); + +MODULE_DESCRIPTION("ASoC WM8782 driver"); +MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 449ea09a193d..082040eda8a2 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1167,6 +1167,7 @@ static int wm8900_resume(struct snd_soc_codec *codec) ret = wm8900_set_fll(codec, 0, fll_in, fll_out); if (ret != 0) { dev_err(codec->dev, "Failed to restart FLL\n"); + kfree(cache); return ret; } } diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9b3bba4df5b3..b085575d4aa5 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2560,6 +2560,7 @@ static __devexit int wm8904_i2c_remove(struct i2c_client *client) static const struct i2c_device_id wm8904_i2c_id[] = { { "wm8904", WM8904 }, { "wm8912", WM8912 }, + { "wm8918", WM8904 }, /* Actually a subset, updates to follow */ { } }; MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c index e2ab4fac2819..423baa9be241 100644 --- a/sound/soc/codecs/wm8915.c +++ b/sound/soc/codecs/wm8915.c @@ -41,14 +41,12 @@ #define HPOUT2L 4 #define HPOUT2R 8 -#define WM8915_NUM_SUPPLIES 6 +#define WM8915_NUM_SUPPLIES 4 static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = { - "DCVDD", "DBVDD", "AVDD1", "AVDD2", "CPVDD", - "MICVDD", }; struct wm8915_priv { @@ -57,6 +55,7 @@ struct wm8915_priv { int ldo1ena; int sysclk; + int sysclk_src; int fll_src; int fll_fref; @@ -76,6 +75,7 @@ struct wm8915_priv { struct wm8915_pdata pdata; int rx_rate[WM8915_AIFS]; + int bclk_rate[WM8915_AIFS]; /* Platform dependant ReTune mobile configuration */ int num_retune_mobile_texts; @@ -113,8 +113,6 @@ WM8915_REGULATOR_EVENT(0) WM8915_REGULATOR_EVENT(1) WM8915_REGULATOR_EVENT(2) WM8915_REGULATOR_EVENT(3) -WM8915_REGULATOR_EVENT(4) -WM8915_REGULATOR_EVENT(5) static const u16 wm8915_reg[WM8915_MAX_REGISTER] = { [WM8915_SOFTWARE_RESET] = 0x8915, @@ -1565,6 +1563,50 @@ static int wm8915_reset(struct snd_soc_codec *codec) return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915); } +static const int bclk_divs[] = { + 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96 +}; + +static void wm8915_update_bclk(struct snd_soc_codec *codec) +{ + struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec); + int aif, best, cur_val, bclk_rate, bclk_reg, i; + + /* Don't bother if we're in a low frequency idle mode that + * can't support audio. + */ + if (wm8915->sysclk < 64000) + return; + + for (aif = 0; aif < WM8915_AIFS; aif++) { + switch (aif) { + case 0: + bclk_reg = WM8915_AIF1_BCLK; + break; + case 1: + bclk_reg = WM8915_AIF2_BCLK; + break; + } + + bclk_rate = wm8915->bclk_rate[aif]; + + /* Pick a divisor for BCLK as close as we can get to ideal */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + bclk_rate = wm8915->sysclk / bclk_divs[best]; + dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", + bclk_divs[best], bclk_rate); + + snd_soc_update_bits(codec, bclk_reg, + WM8915_AIF1_BCLK_DIV_MASK, best); + } +} + static int wm8915_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -1717,10 +1759,6 @@ static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const int bclk_divs[] = { - 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96 -}; - static const int dsp_divs[] = { 48000, 32000, 16000, 8000 }; @@ -1731,17 +1769,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec); - int bits, i, bclk_rate, best, cur_val; + int bits, i, bclk_rate; int aifdata = 0; - int bclk = 0; int lrclk = 0; int dsp = 0; - int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift; - - if (!wm8915->sysclk) { - dev_err(codec->dev, "SYSCLK not configured\n"); - return -EINVAL; - } + int aifdata_reg, lrclk_reg, dsp_shift; switch (dai->id) { case 0: @@ -1753,7 +1785,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1; lrclk_reg = WM8915_AIF1_TX_LRCLK_1; } - bclk_reg = WM8915_AIF1_BCLK; dsp_shift = 0; break; case 1: @@ -1765,7 +1796,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1; lrclk_reg = WM8915_AIF2_TX_LRCLK_1; } - bclk_reg = WM8915_AIF2_BCLK; dsp_shift = WM8915_DSP2_DIV_SHIFT; break; default: @@ -1779,6 +1809,9 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, return bclk_rate; } + wm8915->bclk_rate[dai->id] = bclk_rate; + wm8915->rx_rate[dai->id] = params_rate(params); + /* Needs looking at for TDM */ bits = snd_pcm_format_width(params_format(params)); if (bits < 0) @@ -1796,18 +1829,7 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, } dsp |= i << dsp_shift; - /* Pick a divisor for BCLK as close as we can get to ideal */ - best = 0; - for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { - cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate; - if (cur_val < 0) /* BCLK table is sorted */ - break; - best = i; - } - bclk_rate = wm8915->sysclk / bclk_divs[best]; - dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", - bclk_divs[best], bclk_rate); - bclk |= best; + wm8915_update_bclk(codec); lrclk = bclk_rate / params_rate(params); dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n", @@ -1817,14 +1839,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream, WM8915_AIF1TX_WL_MASK | WM8915_AIF1TX_SLOT_LEN_MASK, aifdata); - snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk); snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK, lrclk); snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2, WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp); - wm8915->rx_rate[dai->id] = params_rate(params); - return 0; } @@ -1838,6 +1857,9 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai, int src; int old; + if (freq == wm8915->sysclk && clk_id == wm8915->sysclk_src) + return 0; + /* Disable SYSCLK while we reconfigure */ old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1) & WM8915_SYSCLK_ENA; snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1, @@ -1882,6 +1904,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai, return -EINVAL; } + wm8915_update_bclk(codec); + snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1, WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK, src << WM8915_SYSCLK_SRC_SHIFT | ratediv); @@ -1889,6 +1913,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai, snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1, WM8915_SYSCLK_ENA, old); + wm8915->sysclk_src = clk_id; + return 0; } @@ -2007,6 +2033,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source, unsigned int Fref, unsigned int Fout) { struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec); + struct i2c_client *i2c = to_i2c_client(codec->dev); struct _fll_div fll_div; unsigned long timeout; int ret, reg; @@ -2093,7 +2120,18 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source, else timeout = msecs_to_jiffies(2); - wait_for_completion_timeout(&wm8915->fll_lock, timeout); + /* Allow substantially longer if we've actually got the IRQ */ + if (i2c->irq) + timeout *= 1000; + + ret = wait_for_completion_timeout(&wm8915->fll_lock, timeout); + + if (ret == 0 && i2c->irq) { + dev_err(codec->dev, "Timed out waiting for FLL\n"); + ret = -ETIMEDOUT; + } else { + ret = 0; + } dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); @@ -2101,7 +2139,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source, wm8915->fll_fout = Fout; wm8915->fll_src = source; - return 0; + return ret; } #ifdef CONFIG_GPIOLIB @@ -2293,6 +2331,12 @@ static void wm8915_micd(struct snd_soc_codec *codec) SND_JACK_HEADSET | SND_JACK_BTN_0); wm8915->jack_mic = true; wm8915->detecting = false; + + /* Increase poll rate to give better responsiveness + * for buttons */ + snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, + WM8915_MICD_RATE_MASK, + 5 << WM8915_MICD_RATE_SHIFT); } /* If we detected a lower impedence during initial startup @@ -2333,15 +2377,17 @@ static void wm8915_micd(struct snd_soc_codec *codec) SND_JACK_HEADPHONE, SND_JACK_HEADSET | SND_JACK_BTN_0); + + /* Increase the detection rate a bit for + * responsiveness. + */ + snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, + WM8915_MICD_RATE_MASK, + 7 << WM8915_MICD_RATE_SHIFT); + wm8915->detecting = false; } } - - /* Increase poll rate to give better responsiveness for buttons */ - if (!wm8915->detecting) - snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, - WM8915_MICD_RATE_MASK, - 5 << WM8915_MICD_RATE_SHIFT); } static irqreturn_t wm8915_irq(int irq, void *data) @@ -2383,6 +2429,20 @@ static irqreturn_t wm8915_irq(int irq, void *data) } } +static irqreturn_t wm8915_edge_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + irqreturn_t val; + + do { + val = wm8915_irq(irq, data); + if (val != IRQ_NONE) + ret = val; + } while (val != IRQ_NONE); + + return ret; +} + static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec) { struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec); @@ -2482,8 +2542,6 @@ static int wm8915_probe(struct snd_soc_codec *codec) wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1; wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2; wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3; - wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4; - wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5; /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) { @@ -2709,8 +2767,14 @@ static int wm8915_probe(struct snd_soc_codec *codec) irq_flags |= IRQF_ONESHOT; - ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq, - irq_flags, "wm8915", codec); + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) + ret = request_threaded_irq(i2c->irq, NULL, + wm8915_edge_irq, + irq_flags, "wm8915", codec); + else + ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq, + irq_flags, "wm8915", codec); + if (ret == 0) { /* Unmask the interrupt */ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL, diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 25580e3ee7c4..056daa0010f9 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -297,8 +297,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec) if (ret) goto error_ret; ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - if (ret) - goto error_ret; error_ret: return ret; @@ -683,8 +681,6 @@ static int wm8940_resume(struct snd_soc_codec *codec) } } ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (ret) - goto error_ret; error_ret: return ret; @@ -730,9 +726,6 @@ static int wm8940_probe(struct snd_soc_codec *codec) if (ret) return ret; ret = wm8940_add_widgets(codec); - if (ret) - return ret; - return ret; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 5e05eed96c38..60d740ebeb5b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -78,6 +78,8 @@ struct wm8962_priv { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif + + int irq; }; /* We can't use the same notifier block for more than one supply and @@ -1982,6 +1984,7 @@ static const unsigned int classd_tlv[] = { 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), }; +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); /* The VU bits for the headphones are in a different register to the mute * bits and only take effect on the PGA if it is actually powered. @@ -2119,6 +2122,18 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4, SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0, classd_tlv), + +SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0), +SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), }; static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { @@ -2184,6 +2199,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + unsigned long timeout; int src; int fll; @@ -2203,9 +2220,19 @@ static int sysclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (fll) + if (fll) { snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, WM8962_FLL_ENA); + if (wm8962->irq) { + timeout = msecs_to_jiffies(5); + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); + + if (timeout == 0) + dev_err(codec->dev, + "Timed out starting FLL\n"); + } + } break; case SND_SOC_DAPM_POST_PMD: @@ -2763,18 +2790,44 @@ static const int bclk_divs[] = { 1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32 }; +static const int sysclk_rates[] = { + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, +}; + static void wm8962_configure_bclk(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int dspclk, i; int clocking2 = 0; + int clocking4 = 0; int aif2 = 0; - if (!wm8962->bclk) { - dev_dbg(codec->dev, "No BCLK rate configured\n"); + if (!wm8962->sysclk_rate) { + dev_dbg(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8962->bclk || !wm8962->lrclk) { + dev_dbg(codec->dev, "No audio clocks configured\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) { + if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) { + clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT; + break; + } + } + + if (i == ARRAY_SIZE(sysclk_rates)) { + dev_err(codec->dev, "Unsupported sysclk ratio %d\n", + wm8962->sysclk_rate / wm8962->lrclk); return; } + snd_soc_update_bits(codec, WM8962_CLOCKING_4, + WM8962_SYSCLK_RATE_MASK, clocking4); + dspclk = snd_soc_read(codec, WM8962_CLOCKING1); if (dspclk < 0) { dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk); @@ -2844,6 +2897,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, /* VMID 2*50k */ snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK, 0x80); + + wm8962_configure_bclk(codec); break; case SND_SOC_BIAS_STANDBY: @@ -2876,8 +2931,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); - - wm8962_configure_bclk(codec); } /* VMID 2*250k */ @@ -2918,10 +2971,6 @@ static const struct { { 96000, 6 }, }; -static const int sysclk_rates[] = { - 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, -}; - static int wm8962_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -2929,41 +2978,27 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - int rate = params_rate(params); int i; int aif0 = 0; int adctl3 = 0; - int clocking4 = 0; wm8962->bclk = snd_soc_params_to_bclk(params); wm8962->lrclk = params_rate(params); for (i = 0; i < ARRAY_SIZE(sr_vals); i++) { - if (sr_vals[i].rate == rate) { + if (sr_vals[i].rate == wm8962->lrclk) { adctl3 |= sr_vals[i].reg; break; } } if (i == ARRAY_SIZE(sr_vals)) { - dev_err(codec->dev, "Unsupported rate %dHz\n", rate); + dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk); return -EINVAL; } - if (rate % 8000 == 0) + if (wm8962->lrclk % 8000 == 0) adctl3 |= WM8962_SAMPLE_RATE_INT_MODE; - for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) { - if (sysclk_rates[i] == wm8962->sysclk_rate / rate) { - clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT; - break; - } - } - if (i == ARRAY_SIZE(sysclk_rates)) { - dev_err(codec->dev, "Unsupported sysclk ratio %d\n", - wm8962->sysclk_rate / rate); - return -EINVAL; - } - switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; @@ -2985,8 +3020,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3, WM8962_SAMPLE_RATE_INT_MODE | WM8962_SAMPLE_RATE_MASK, adctl3); - snd_soc_update_bits(codec, WM8962_CLOCKING_4, - WM8962_SYSCLK_RATE_MASK, clocking4); wm8962_configure_bclk(codec); @@ -3261,16 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); - /* This should be a massive overestimate */ - timeout = msecs_to_jiffies(1); + ret = 0; - wait_for_completion_timeout(&wm8962->fll_lock, timeout); + if (fll1 & WM8962_FLL_ENA) { + /* This should be a massive overestimate but go even + * higher if we'll error out + */ + if (wm8962->irq) + timeout = msecs_to_jiffies(5); + else + timeout = msecs_to_jiffies(1); + + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); + + if (timeout == 0 && wm8962->irq) { + dev_err(codec->dev, "FLL lock timed out"); + ret = -ETIMEDOUT; + } + } wm8962->fll_fref = Fref; wm8962->fll_fout = Fout; wm8962->fll_src = source; - return 0; + return ret; } static int wm8962_mute(struct snd_soc_dai *dai, int mute) @@ -3361,6 +3409,9 @@ static irqreturn_t wm8962_irq(int irq, void *data) active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); active &= ~mask; + /* Acknowledge the interrupts */ + snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); + if (active & WM8962_FLL_LOCK_EINT) { dev_dbg(codec->dev, "FLL locked\n"); complete(&wm8962->fll_lock); @@ -3385,9 +3436,6 @@ static irqreturn_t wm8962_irq(int irq, void *data) msecs_to_jiffies(250)); } - /* Acknowledge the interrupts */ - snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); - return IRQ_HANDLED; } @@ -3731,8 +3779,6 @@ static int wm8962_probe(struct snd_soc_codec *codec) int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); - struct i2c_client *i2c = container_of(codec->dev, struct i2c_client, - dev); u16 *reg_cache = codec->reg_cache; int i, trigger, irq_pol; bool dmicclk, dmicdat; @@ -3871,6 +3917,9 @@ static int wm8962_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME, WM8962_HPOUT_VU, WM8962_HPOUT_VU); + /* Stereo control for EQ */ + snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0); + wm8962_add_widgets(codec); /* Save boards having to disable DMIC when not in use */ @@ -3899,7 +3948,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) wm8962_init_beep(codec); wm8962_init_gpio(codec); - if (i2c->irq) { + if (wm8962->irq) { if (pdata && pdata->irq_active_low) { trigger = IRQF_TRIGGER_LOW; irq_pol = WM8962_IRQ_POL; @@ -3911,12 +3960,13 @@ static int wm8962_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL, WM8962_IRQ_POL, irq_pol); - ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq, + ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq, trigger | IRQF_ONESHOT, "wm8962", codec); if (ret != 0) { dev_err(codec->dev, "Failed to request IRQ %d: %d\n", - i2c->irq, ret); + wm8962->irq, ret); + wm8962->irq = 0; /* Non-fatal */ } else { /* Enable some IRQs by default */ @@ -3941,12 +3991,10 @@ err: static int wm8962_remove(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *i2c = container_of(codec->dev, struct i2c_client, - dev); int i; - if (i2c->irq) - free_irq(i2c->irq, codec); + if (wm8962->irq) + free_irq(wm8962->irq, codec); cancel_delayed_work_sync(&wm8962->mic_work); @@ -3986,6 +4034,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8962); + wm8962->irq = i2c->irq; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8962, &wm8962_dai, 1); if (ret < 0) diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c new file mode 100644 index 000000000000..17f04ec2b940 --- /dev/null +++ b/sound/soc/codecs/wm8983.c @@ -0,0 +1,1203 @@ +/* + * wm8983.c -- WM8983 ALSA SoC Audio driver + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8983.h" + +static const u16 wm8983_reg_defs[WM8983_MAX_REGISTER + 1] = { + [0x00] = 0x0000, /* R0 - Software Reset */ + [0x01] = 0x0000, /* R1 - Power management 1 */ + [0x02] = 0x0000, /* R2 - Power management 2 */ + [0x03] = 0x0000, /* R3 - Power management 3 */ + [0x04] = 0x0050, /* R4 - Audio Interface */ + [0x05] = 0x0000, /* R5 - Companding control */ + [0x06] = 0x0140, /* R6 - Clock Gen control */ + [0x07] = 0x0000, /* R7 - Additional control */ + [0x08] = 0x0000, /* R8 - GPIO Control */ + [0x09] = 0x0000, /* R9 - Jack Detect Control 1 */ + [0x0A] = 0x0000, /* R10 - DAC Control */ + [0x0B] = 0x00FF, /* R11 - Left DAC digital Vol */ + [0x0C] = 0x00FF, /* R12 - Right DAC digital vol */ + [0x0D] = 0x0000, /* R13 - Jack Detect Control 2 */ + [0x0E] = 0x0100, /* R14 - ADC Control */ + [0x0F] = 0x00FF, /* R15 - Left ADC Digital Vol */ + [0x10] = 0x00FF, /* R16 - Right ADC Digital Vol */ + [0x12] = 0x012C, /* R18 - EQ1 - low shelf */ + [0x13] = 0x002C, /* R19 - EQ2 - peak 1 */ + [0x14] = 0x002C, /* R20 - EQ3 - peak 2 */ + [0x15] = 0x002C, /* R21 - EQ4 - peak 3 */ + [0x16] = 0x002C, /* R22 - EQ5 - high shelf */ + [0x18] = 0x0032, /* R24 - DAC Limiter 1 */ + [0x19] = 0x0000, /* R25 - DAC Limiter 2 */ + [0x1B] = 0x0000, /* R27 - Notch Filter 1 */ + [0x1C] = 0x0000, /* R28 - Notch Filter 2 */ + [0x1D] = 0x0000, /* R29 - Notch Filter 3 */ + [0x1E] = 0x0000, /* R30 - Notch Filter 4 */ + [0x20] = 0x0038, /* R32 - ALC control 1 */ + [0x21] = 0x000B, /* R33 - ALC control 2 */ + [0x22] = 0x0032, /* R34 - ALC control 3 */ + [0x23] = 0x0000, /* R35 - Noise Gate */ + [0x24] = 0x0008, /* R36 - PLL N */ + [0x25] = 0x000C, /* R37 - PLL K 1 */ + [0x26] = 0x0093, /* R38 - PLL K 2 */ + [0x27] = 0x00E9, /* R39 - PLL K 3 */ + [0x29] = 0x0000, /* R41 - 3D control */ + [0x2A] = 0x0000, /* R42 - OUT4 to ADC */ + [0x2B] = 0x0000, /* R43 - Beep control */ + [0x2C] = 0x0033, /* R44 - Input ctrl */ + [0x2D] = 0x0010, /* R45 - Left INP PGA gain ctrl */ + [0x2E] = 0x0010, /* R46 - Right INP PGA gain ctrl */ + [0x2F] = 0x0100, /* R47 - Left ADC BOOST ctrl */ + [0x30] = 0x0100, /* R48 - Right ADC BOOST ctrl */ + [0x31] = 0x0002, /* R49 - Output ctrl */ + [0x32] = 0x0001, /* R50 - Left mixer ctrl */ + [0x33] = 0x0001, /* R51 - Right mixer ctrl */ + [0x34] = 0x0039, /* R52 - LOUT1 (HP) volume ctrl */ + [0x35] = 0x0039, /* R53 - ROUT1 (HP) volume ctrl */ + [0x36] = 0x0039, /* R54 - LOUT2 (SPK) volume ctrl */ + [0x37] = 0x0039, /* R55 - ROUT2 (SPK) volume ctrl */ + [0x38] = 0x0001, /* R56 - OUT3 mixer ctrl */ + [0x39] = 0x0001, /* R57 - OUT4 (MONO) mix ctrl */ + [0x3D] = 0x0000 /* R61 - BIAS CTRL */ +}; + +static const struct wm8983_reg_access { + u16 read; /* Mask of readable bits */ + u16 write; /* Mask of writable bits */ +} wm8983_access_masks[WM8983_MAX_REGISTER + 1] = { + [0x00] = { 0x0000, 0x01FF }, /* R0 - Software Reset */ + [0x01] = { 0x0000, 0x01FF }, /* R1 - Power management 1 */ + [0x02] = { 0x0000, 0x01FF }, /* R2 - Power management 2 */ + [0x03] = { 0x0000, 0x01EF }, /* R3 - Power management 3 */ + [0x04] = { 0x0000, 0x01FF }, /* R4 - Audio Interface */ + [0x05] = { 0x0000, 0x003F }, /* R5 - Companding control */ + [0x06] = { 0x0000, 0x01FD }, /* R6 - Clock Gen control */ + [0x07] = { 0x0000, 0x000F }, /* R7 - Additional control */ + [0x08] = { 0x0000, 0x003F }, /* R8 - GPIO Control */ + [0x09] = { 0x0000, 0x0070 }, /* R9 - Jack Detect Control 1 */ + [0x0A] = { 0x0000, 0x004F }, /* R10 - DAC Control */ + [0x0B] = { 0x0000, 0x01FF }, /* R11 - Left DAC digital Vol */ + [0x0C] = { 0x0000, 0x01FF }, /* R12 - Right DAC digital vol */ + [0x0D] = { 0x0000, 0x00FF }, /* R13 - Jack Detect Control 2 */ + [0x0E] = { 0x0000, 0x01FB }, /* R14 - ADC Control */ + [0x0F] = { 0x0000, 0x01FF }, /* R15 - Left ADC Digital Vol */ + [0x10] = { 0x0000, 0x01FF }, /* R16 - Right ADC Digital Vol */ + [0x12] = { 0x0000, 0x017F }, /* R18 - EQ1 - low shelf */ + [0x13] = { 0x0000, 0x017F }, /* R19 - EQ2 - peak 1 */ + [0x14] = { 0x0000, 0x017F }, /* R20 - EQ3 - peak 2 */ + [0x15] = { 0x0000, 0x017F }, /* R21 - EQ4 - peak 3 */ + [0x16] = { 0x0000, 0x007F }, /* R22 - EQ5 - high shelf */ + [0x18] = { 0x0000, 0x01FF }, /* R24 - DAC Limiter 1 */ + [0x19] = { 0x0000, 0x007F }, /* R25 - DAC Limiter 2 */ + [0x1B] = { 0x0000, 0x01FF }, /* R27 - Notch Filter 1 */ + [0x1C] = { 0x0000, 0x017F }, /* R28 - Notch Filter 2 */ + [0x1D] = { 0x0000, 0x017F }, /* R29 - Notch Filter 3 */ + [0x1E] = { 0x0000, 0x017F }, /* R30 - Notch Filter 4 */ + [0x20] = { 0x0000, 0x01BF }, /* R32 - ALC control 1 */ + [0x21] = { 0x0000, 0x00FF }, /* R33 - ALC control 2 */ + [0x22] = { 0x0000, 0x01FF }, /* R34 - ALC control 3 */ + [0x23] = { 0x0000, 0x000F }, /* R35 - Noise Gate */ + [0x24] = { 0x0000, 0x001F }, /* R36 - PLL N */ + [0x25] = { 0x0000, 0x003F }, /* R37 - PLL K 1 */ + [0x26] = { 0x0000, 0x01FF }, /* R38 - PLL K 2 */ + [0x27] = { 0x0000, 0x01FF }, /* R39 - PLL K 3 */ + [0x29] = { 0x0000, 0x000F }, /* R41 - 3D control */ + [0x2A] = { 0x0000, 0x01E7 }, /* R42 - OUT4 to ADC */ + [0x2B] = { 0x0000, 0x01BF }, /* R43 - Beep control */ + [0x2C] = { 0x0000, 0x0177 }, /* R44 - Input ctrl */ + [0x2D] = { 0x0000, 0x01FF }, /* R45 - Left INP PGA gain ctrl */ + [0x2E] = { 0x0000, 0x01FF }, /* R46 - Right INP PGA gain ctrl */ + [0x2F] = { 0x0000, 0x0177 }, /* R47 - Left ADC BOOST ctrl */ + [0x30] = { 0x0000, 0x0177 }, /* R48 - Right ADC BOOST ctrl */ + [0x31] = { 0x0000, 0x007F }, /* R49 - Output ctrl */ + [0x32] = { 0x0000, 0x01FF }, /* R50 - Left mixer ctrl */ + [0x33] = { 0x0000, 0x01FF }, /* R51 - Right mixer ctrl */ + [0x34] = { 0x0000, 0x01FF }, /* R52 - LOUT1 (HP) volume ctrl */ + [0x35] = { 0x0000, 0x01FF }, /* R53 - ROUT1 (HP) volume ctrl */ + [0x36] = { 0x0000, 0x01FF }, /* R54 - LOUT2 (SPK) volume ctrl */ + [0x37] = { 0x0000, 0x01FF }, /* R55 - ROUT2 (SPK) volume ctrl */ + [0x38] = { 0x0000, 0x004F }, /* R56 - OUT3 mixer ctrl */ + [0x39] = { 0x0000, 0x00FF }, /* R57 - OUT4 (MONO) mix ctrl */ + [0x3D] = { 0x0000, 0x0100 } /* R61 - BIAS CTRL */ +}; + +/* vol/gain update regs */ +static const int vol_update_regs[] = { + WM8983_LEFT_DAC_DIGITAL_VOL, + WM8983_RIGHT_DAC_DIGITAL_VOL, + WM8983_LEFT_ADC_DIGITAL_VOL, + WM8983_RIGHT_ADC_DIGITAL_VOL, + WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, + WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, + WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL +}; + +struct wm8983_priv { + enum snd_soc_control_type control_type; + u32 sysclk; + u32 bclk; +}; + +static const struct { + int div; + int ratio; +} fs_ratios[] = { + { 10, 128 }, + { 15, 192 }, + { 20, 256 }, + { 30, 384 }, + { 40, 512 }, + { 60, 768 }, + { 80, 1024 }, + { 120, 1536 } +}; + +static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 }; + +static const int bclk_divs[] = { + 1, 2, 4, 8, 16, 32 +}; + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); + +static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" }; +static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, + alc_sel_text); + +static const char *alc_mode_text[] = { "ALC", "Limiter" }; +static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, + alc_mode_text); + +static const char *filter_mode_text[] = { "Audio", "Application" }; +static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7, + filter_mode_text); + +static const char *eq_bw_text[] = { "Narrow", "Wide" }; +static const char *eqmode_text[] = { "Capture", "Playback" }; +static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); + +static const char *eq1_cutoff_text[] = { + "80Hz", "105Hz", "135Hz", "175Hz" +}; +static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5, + eq1_cutoff_text); +static const char *eq2_cutoff_text[] = { + "230Hz", "300Hz", "385Hz", "500Hz" +}; +static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text); +static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, + eq2_cutoff_text); +static const char *eq3_cutoff_text[] = { + "650Hz", "850Hz", "1.1kHz", "1.4kHz" +}; +static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text); +static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, + eq3_cutoff_text); +static const char *eq4_cutoff_text[] = { + "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" +}; +static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text); +static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, + eq4_cutoff_text); +static const char *eq5_cutoff_text[] = { + "5.3kHz", "6.9kHz", "9kHz", "11.7kHz" +}; +static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5, + eq5_cutoff_text); + +static const char *speaker_mode_text[] = { "Class A/B", "Class D" }; +static const SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text); + +static const char *depth_3d_text[] = { + "Off", + "6.67%", + "13.3%", + "20%", + "26.7%", + "33.3%", + "40%", + "46.6%", + "53.3%", + "60%", + "66.7%", + "73.3%", + "80%", + "86.7%", + "93.3%", + "100%" +}; +static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0, + depth_3d_text); + +static const struct snd_kcontrol_new wm8983_snd_controls[] = { + SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL, + 0, 1, 0), + + SOC_ENUM("ALC Capture Function", alc_sel), + SOC_SINGLE_TLV("ALC Capture Max Volume", WM8983_ALC_CONTROL_1, + 3, 7, 0, alc_max_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", WM8983_ALC_CONTROL_1, + 0, 7, 0, alc_min_tlv), + SOC_SINGLE_TLV("ALC Capture Target Volume", WM8983_ALC_CONTROL_2, + 0, 15, 0, alc_tar_tlv), + SOC_SINGLE("ALC Capture Attack", WM8983_ALC_CONTROL_3, 0, 10, 0), + SOC_SINGLE("ALC Capture Hold", WM8983_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Capture Decay", WM8983_ALC_CONTROL_3, 4, 10, 0), + SOC_ENUM("ALC Mode", alc_mode), + SOC_SINGLE("ALC Capture NG Switch", WM8983_NOISE_GATE, + 3, 1, 0), + SOC_SINGLE("ALC Capture NG Threshold", WM8983_NOISE_GATE, + 0, 7, 1), + + SOC_DOUBLE_R_TLV("Capture Volume", WM8983_LEFT_ADC_DIGITAL_VOL, + WM8983_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv), + SOC_DOUBLE_R("Capture PGA ZC Switch", WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv), + + SOC_DOUBLE_R_TLV("Capture PGA Boost Volume", + WM8983_LEFT_ADC_BOOST_CTRL, WM8983_RIGHT_ADC_BOOST_CTRL, + 8, 1, 0, pga_boost_tlv), + + SOC_DOUBLE("ADC Inversion Switch", WM8983_ADC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8983_ADC_CONTROL, 8, 1, 0), + + SOC_DOUBLE_R_TLV("Playback Volume", WM8983_LEFT_DAC_DIGITAL_VOL, + WM8983_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv), + + SOC_SINGLE("DAC Playback Limiter Switch", WM8983_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Decay", WM8983_DAC_LIMITER_1, 4, 10, 0), + SOC_SINGLE("DAC Playback Limiter Attack", WM8983_DAC_LIMITER_1, 0, 11, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8983_DAC_LIMITER_2, + 4, 7, 1, lim_thresh_tlv), + SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8983_DAC_LIMITER_2, + 0, 12, 0, lim_boost_tlv), + SOC_DOUBLE("DAC Inversion Switch", WM8983_DAC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("DAC Auto Mute Switch", WM8983_DAC_CONTROL, 2, 1, 0), + SOC_SINGLE("DAC 128x Oversampling Switch", WM8983_DAC_CONTROL, 3, 1, 0), + + SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Headphone Switch", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 6, 1, 1), + + SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Speaker Switch", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1), + + SOC_SINGLE("OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 6, 1, 1), + + SOC_SINGLE("OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 6, 1, 1), + + SOC_SINGLE("High Pass Filter Switch", WM8983_ADC_CONTROL, 8, 1, 0), + SOC_ENUM("High Pass Filter Mode", filter_mode), + SOC_SINGLE("High Pass Filter Cutoff", WM8983_ADC_CONTROL, 4, 7, 0), + + SOC_DOUBLE_R_TLV("Aux Bypass Volume", + WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 6, 7, 0, + aux_tlv), + + SOC_DOUBLE_R_TLV("Input PGA Bypass Volume", + WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 2, 7, 0, + bypass_tlv), + + SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put), + SOC_ENUM("EQ1 Cutoff", eq1_cutoff), + SOC_SINGLE_TLV("EQ1 Volume", WM8983_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ2 Bandwith", eq2_bw), + SOC_ENUM("EQ2 Cutoff", eq2_cutoff), + SOC_SINGLE_TLV("EQ2 Volume", WM8983_EQ2_PEAK_1, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ3 Bandwith", eq3_bw), + SOC_ENUM("EQ3 Cutoff", eq3_cutoff), + SOC_SINGLE_TLV("EQ3 Volume", WM8983_EQ3_PEAK_2, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ4 Bandwith", eq4_bw), + SOC_ENUM("EQ4 Cutoff", eq4_cutoff), + SOC_SINGLE_TLV("EQ4 Volume", WM8983_EQ4_PEAK_3, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ5 Cutoff", eq5_cutoff), + SOC_SINGLE_TLV("EQ5 Volume", WM8983_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv), + + SOC_ENUM("3D Depth", depth_3d), + + SOC_ENUM("Speaker Mode", speaker_mode) +}; + +static const struct snd_kcontrol_new left_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8983_LEFT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Switch", WM8983_LEFT_MIXER_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8983_LEFT_MIXER_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8983_RIGHT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Switch", WM8983_RIGHT_MIXER_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8983_RIGHT_MIXER_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", WM8983_INPUT_CTRL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", WM8983_INPUT_CTRL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 4, 1, 0), +}; + +static const struct snd_kcontrol_new left_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("L2 Volume", WM8983_LEFT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8983_LEFT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_kcontrol_new out3_mixer[] = { + SOC_DAPM_SINGLE("LMIX2OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 1, 1, 0), + SOC_DAPM_SINGLE("LDAC2OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new out4_mixer[] = { + SOC_DAPM_SINGLE("LMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 4, 1, 0), + SOC_DAPM_SINGLE("RMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 1, 1, 0), + SOC_DAPM_SINGLE("LDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 3, 1, 0), + SOC_DAPM_SINGLE("RDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("R2 Volume", WM8983_RIGHT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8983_RIGHT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_soc_dapm_widget wm8983_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8983_POWER_MANAGEMENT_3, + 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8983_POWER_MANAGEMENT_3, + 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8983_POWER_MANAGEMENT_2, + 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8983_POWER_MANAGEMENT_2, + 1, 0), + + SND_SOC_DAPM_MIXER("Left Output Mixer", WM8983_POWER_MANAGEMENT_3, + 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)), + SND_SOC_DAPM_MIXER("Right Output Mixer", WM8983_POWER_MANAGEMENT_3, + 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)), + + SND_SOC_DAPM_MIXER("Left Input Mixer", WM8983_POWER_MANAGEMENT_2, + 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)), + SND_SOC_DAPM_MIXER("Right Input Mixer", WM8983_POWER_MANAGEMENT_2, + 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)), + + SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8983_POWER_MANAGEMENT_2, + 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)), + SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8983_POWER_MANAGEMENT_2, + 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)), + + SND_SOC_DAPM_MIXER("OUT3 Mixer", WM8983_POWER_MANAGEMENT_1, + 6, 0, out3_mixer, ARRAY_SIZE(out3_mixer)), + + SND_SOC_DAPM_MIXER("OUT4 Mixer", WM8983_POWER_MANAGEMENT_1, + 7, 0, out4_mixer, ARRAY_SIZE(out4_mixer)), + + SND_SOC_DAPM_PGA("Left Capture PGA", WM8983_LEFT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", WM8983_RIGHT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", WM8983_POWER_MANAGEMENT_2, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", WM8983_POWER_MANAGEMENT_2, + 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", WM8983_POWER_MANAGEMENT_3, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", WM8983_POWER_MANAGEMENT_3, + 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("OUT3 Out", WM8983_POWER_MANAGEMENT_3, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("OUT4 Out", WM8983_POWER_MANAGEMENT_3, + 8, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8983_POWER_MANAGEMENT_1, 4, 0), + + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_INPUT("LIP"), + SND_SOC_DAPM_INPUT("RIN"), + SND_SOC_DAPM_INPUT("RIP"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("OUT4") +}; + +static const struct snd_soc_dapm_route wm8983_audio_map[] = { + { "OUT3 Mixer", "LMIX2OUT3 Switch", "Left Output Mixer" }, + { "OUT3 Mixer", "LDAC2OUT3 Switch", "Left DAC" }, + + { "OUT3 Out", NULL, "OUT3 Mixer" }, + { "OUT3", NULL, "OUT3 Out" }, + + { "OUT4 Mixer", "LMIX2OUT4 Switch", "Left Output Mixer" }, + { "OUT4 Mixer", "RMIX2OUT4 Switch", "Right Output Mixer" }, + { "OUT4 Mixer", "LDAC2OUT4 Switch", "Left DAC" }, + { "OUT4 Mixer", "RDAC2OUT4 Switch", "Right DAC" }, + + { "OUT4 Out", NULL, "OUT4 Mixer" }, + { "OUT4", NULL, "OUT4 Out" }, + + { "Right Output Mixer", "PCM Switch", "Right DAC" }, + { "Right Output Mixer", "Aux Switch", "AUXR" }, + { "Right Output Mixer", "Line Switch", "Right Boost Mixer" }, + + { "Left Output Mixer", "PCM Switch", "Left DAC" }, + { "Left Output Mixer", "Aux Switch", "AUXL" }, + { "Left Output Mixer", "Line Switch", "Left Boost Mixer" }, + + { "Right Headphone Out", NULL, "Right Output Mixer" }, + { "HPR", NULL, "Right Headphone Out" }, + + { "Left Headphone Out", NULL, "Left Output Mixer" }, + { "HPL", NULL, "Left Headphone Out" }, + + { "Right Speaker Out", NULL, "Right Output Mixer" }, + { "SPKR", NULL, "Right Speaker Out" }, + + { "Left Speaker Out", NULL, "Left Output Mixer" }, + { "SPKL", NULL, "Left Speaker Out" }, + + { "Right ADC", NULL, "Right Boost Mixer" }, + + { "Right Boost Mixer", "AUXR Volume", "AUXR" }, + { "Right Boost Mixer", NULL, "Right Capture PGA" }, + { "Right Boost Mixer", "R2 Volume", "R2" }, + + { "Left ADC", NULL, "Left Boost Mixer" }, + + { "Left Boost Mixer", "AUXL Volume", "AUXL" }, + { "Left Boost Mixer", NULL, "Left Capture PGA" }, + { "Left Boost Mixer", "L2 Volume", "L2" }, + + { "Right Capture PGA", NULL, "Right Input Mixer" }, + { "Left Capture PGA", NULL, "Left Input Mixer" }, + + { "Right Input Mixer", "R2 Switch", "R2" }, + { "Right Input Mixer", "MicN Switch", "RIN" }, + { "Right Input Mixer", "MicP Switch", "RIP" }, + + { "Left Input Mixer", "L2 Switch", "L2" }, + { "Left Input Mixer", "MicN Switch", "LIN" }, + { "Left Input Mixer", "MicP Switch", "LIP" }, +}; + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg; + + reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF); + if (reg & WM8983_EQ3DMODE) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int regpwr2, regpwr3; + unsigned int reg_eq; + + if (ucontrol->value.integer.value[0] != 0 + && ucontrol->value.integer.value[0] != 1) + return -EINVAL; + + reg_eq = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF); + switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) { + case 0: + if (!ucontrol->value.integer.value[0]) + return 0; + break; + case 1: + if (ucontrol->value.integer.value[0]) + return 0; + break; + } + + regpwr2 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_2); + regpwr3 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_3); + /* disable the DACs and ADCs */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_2, + WM8983_ADCENR_MASK | WM8983_ADCENL_MASK, 0); + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_3, + WM8983_DACENR_MASK | WM8983_DACENL_MASK, 0); + /* set the desired eqmode */ + snd_soc_update_bits(codec, WM8983_EQ1_LOW_SHELF, + WM8983_EQ3DMODE_MASK, + ucontrol->value.integer.value[0] + << WM8983_EQ3DMODE_SHIFT); + /* restore DAC/ADC configuration */ + snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, regpwr2); + snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, regpwr3); + return 0; +} + +static int wm8983_readable(struct snd_soc_codec *codec, unsigned int reg) +{ + if (reg > WM8983_MAX_REGISTER) + return 0; + + return wm8983_access_masks[reg].read != 0; +} + +static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + return snd_soc_update_bits(codec, WM8983_DAC_CONTROL, + WM8983_SOFTMUTE_MASK, + !!mute << WM8983_SOFTMUTE_SHIFT); +} + +static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u16 format, master, bcp, lrp; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + format = 0x3; + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE, + WM8983_FMT_MASK, format << WM8983_FMT_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL, + WM8983_MS_MASK, master << WM8983_MS_SHIFT); + + /* FIXME: We don't currently support DSP A/B modes */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + dev_err(dai->dev, "DSP A/B modes are not supported\n"); + return -EINVAL; + default: + break; + } + + bcp = lrp = 0; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bcp = lrp = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + bcp = 1; + break; + case SND_SOC_DAIFMT_NB_IF: + lrp = 1; + break; + default: + dev_err(dai->dev, "Unknown polarity configuration\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE, + WM8983_LRCP_MASK, lrp << WM8983_LRCP_SHIFT); + snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE, + WM8983_BCP_MASK, bcp << WM8983_BCP_SHIFT); + return 0; +} + +static int wm8983_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int i; + struct snd_soc_codec *codec = dai->codec; + struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec); + u16 blen, srate_idx; + u32 tmp; + int srate_best; + int ret; + + ret = snd_soc_params_to_bclk(params); + if (ret < 0) { + dev_err(codec->dev, "Failed to convert params to bclk: %d\n", ret); + return ret; + } + + wm8983->bclk = ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + blen = 0x0; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + blen = 0x1; + break; + case SNDRV_PCM_FORMAT_S24_LE: + blen = 0x2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + blen = 0x3; + break; + default: + dev_err(dai->dev, "Unsupported word length %u\n", + params_format(params)); + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE, + WM8983_WL_MASK, blen << WM8983_WL_SHIFT); + + /* + * match to the nearest possible sample rate and rely + * on the array index to configure the SR register + */ + srate_idx = 0; + srate_best = abs(srates[0] - params_rate(params)); + for (i = 1; i < ARRAY_SIZE(srates); ++i) { + if (abs(srates[i] - params_rate(params)) >= srate_best) + continue; + srate_idx = i; + srate_best = abs(srates[i] - params_rate(params)); + } + + dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]); + snd_soc_update_bits(codec, WM8983_ADDITIONAL_CONTROL, + WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT); + + dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk); + dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8983->sysclk); + + for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) { + if (wm8983->sysclk / params_rate(params) + == fs_ratios[i].ratio) + break; + } + + if (i == ARRAY_SIZE(fs_ratios)) { + dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n", + wm8983->sysclk, params_rate(params)); + return -EINVAL; + } + + dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio); + snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL, + WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT); + + /* select the appropriate bclk divider */ + tmp = (wm8983->sysclk / fs_ratios[i].div) * 10; + for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) { + if (wm8983->bclk == tmp / bclk_divs[i]) + break; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + dev_err(dai->dev, "No matching BCLK divider found\n"); + return -EINVAL; + } + + dev_dbg(dai->dev, "BCLK div = %d\n", i); + snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL, + WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT); + + return 0; +} + +struct pll_div { + u32 div2:1; + u32 n:4; + u32 k:24; +}; + +#define FIXED_PLL_SIZE ((1ULL << 24) * 10) +static int pll_factors(struct pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned long int K, Ndiv, Nmod; + + pll_div->div2 = 0; + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } + + if (Ndiv < 6 || Ndiv > 12) { + printk(KERN_ERR "%s: WM8983 N value is not within" + " the recommended range: %lu\n", __func__, Ndiv); + return -EINVAL; + } + pll_div->n = Ndiv; + + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (u64)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xffffffff; + if ((K % 10) >= 5) + K += 5; + K /= 10; + pll_div->k = K; + return 0; +} + +static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + int ret; + struct snd_soc_codec *codec; + struct pll_div pll_div; + + codec = dai->codec; + if (freq_in && freq_out) { + ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in); + if (ret) + return ret; + } + + /* disable the PLL before re-programming it */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, 0); + + if (!freq_in || !freq_out) + return 0; + + /* set PLLN and PRESCALE */ + snd_soc_write(codec, WM8983_PLL_N, + (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT) + | pll_div.n); + /* set PLLK */ + snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff); + snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18)); + /* enable the PLL */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, WM8983_PLLEN); + return 0; +} + +static int wm8983_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8983_CLKSRC_MCLK: + snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL, + WM8983_CLKSEL_MASK, 0); + break; + case WM8983_CLKSRC_PLL: + snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL, + WM8983_CLKSEL_MASK, WM8983_CLKSEL); + break; + default: + dev_err(dai->dev, "Unknown clock source: %d\n", clk_id); + return -EINVAL; + } + + wm8983->sysclk = freq; + return 0; +} + +static int wm8983_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* VMID at 100k */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 1 << WM8983_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_cache_sync(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + /* enable anti-pop features */ + snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC, + WM8983_POBCTRL_MASK | WM8983_DELEN_MASK, + WM8983_POBCTRL | WM8983_DELEN); + /* enable thermal shutdown */ + snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL, + WM8983_TSDEN_MASK, WM8983_TSDEN); + /* enable BIASEN */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_BIASEN_MASK, WM8983_BIASEN); + /* VMID at 100k */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 1 << WM8983_VMIDSEL_SHIFT); + msleep(250); + /* disable anti-pop features */ + snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC, + WM8983_POBCTRL_MASK | + WM8983_DELEN_MASK, 0); + } + + /* VMID at 500k */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 2 << WM8983_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_OFF: + /* disable thermal shutdown */ + snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL, + WM8983_TSDEN_MASK, 0); + /* disable VMIDSEL and BIASEN */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK | WM8983_BIASEN_MASK, + 0); + /* wait for VMID to discharge */ + msleep(100); + snd_soc_write(codec, WM8983_POWER_MANAGEMENT_1, 0); + snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, 0); + snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, 0); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#ifdef CONFIG_PM +static int wm8983_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8983_resume(struct snd_soc_codec *codec) +{ + wm8983_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define wm8983_suspend NULL +#define wm8983_resume NULL +#endif + +static int wm8983_remove(struct snd_soc_codec *codec) +{ + wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8983_probe(struct snd_soc_codec *codec) +{ + int ret; + struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec); + int i; + + ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8983->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); + return ret; + } + + ret = snd_soc_write(codec, WM8983_SOFTWARE_RESET, 0x8983); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + /* set the vol/gain update bits */ + for (i = 0; i < ARRAY_SIZE(vol_update_regs); ++i) + snd_soc_update_bits(codec, vol_update_regs[i], + 0x100, 0x100); + + /* mute all outputs and set PGAs to minimum gain */ + for (i = WM8983_LOUT1_HP_VOLUME_CTRL; + i <= WM8983_OUT4_MONO_MIX_CTRL; ++i) + snd_soc_update_bits(codec, i, 0x40, 0x40); + + /* enable soft mute */ + snd_soc_update_bits(codec, WM8983_DAC_CONTROL, + WM8983_SOFTMUTE_MASK, + WM8983_SOFTMUTE); + + /* enable BIASCUT */ + snd_soc_update_bits(codec, WM8983_BIAS_CTRL, + WM8983_BIASCUT, WM8983_BIASCUT); + return 0; +} + +static struct snd_soc_dai_ops wm8983_dai_ops = { + .digital_mute = wm8983_dac_mute, + .hw_params = wm8983_hw_params, + .set_fmt = wm8983_set_fmt, + .set_sysclk = wm8983_set_sysclk, + .set_pll = wm8983_set_pll +}; + +#define WM8983_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8983_dai = { + .name = "wm8983-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8983_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8983_FORMATS, + }, + .ops = &wm8983_dai_ops, + .symmetric_rates = 1 +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8983 = { + .probe = wm8983_probe, + .remove = wm8983_remove, + .suspend = wm8983_suspend, + .resume = wm8983_resume, + .set_bias_level = wm8983_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm8983_reg_defs), + .reg_word_size = sizeof(u16), + .reg_cache_default = wm8983_reg_defs, + .controls = wm8983_snd_controls, + .num_controls = ARRAY_SIZE(wm8983_snd_controls), + .dapm_widgets = wm8983_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets), + .dapm_routes = wm8983_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map), + .readable_register = wm8983_readable +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8983_spi_probe(struct spi_device *spi) +{ + struct wm8983_priv *wm8983; + int ret; + + wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL); + if (!wm8983) + return -ENOMEM; + + wm8983->control_type = SND_SOC_SPI; + spi_set_drvdata(spi, wm8983); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8983, &wm8983_dai, 1); + if (ret < 0) + kfree(wm8983); + return ret; +} + +static int __devexit wm8983_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + kfree(spi_get_drvdata(spi)); + return 0; +} + +static struct spi_driver wm8983_spi_driver = { + .driver = { + .name = "wm8983", + .owner = THIS_MODULE, + }, + .probe = wm8983_spi_probe, + .remove = __devexit_p(wm8983_spi_remove) +}; +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int wm8983_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8983_priv *wm8983; + int ret; + + wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL); + if (!wm8983) + return -ENOMEM; + + wm8983->control_type = SND_SOC_I2C; + i2c_set_clientdata(i2c, wm8983); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8983, &wm8983_dai, 1); + if (ret < 0) + kfree(wm8983); + return ret; +} + +static __devexit int wm8983_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id wm8983_i2c_id[] = { + { "wm8983", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id); + +static struct i2c_driver wm8983_i2c_driver = { + .driver = { + .name = "wm8983", + .owner = THIS_MODULE, + }, + .probe = wm8983_i2c_probe, + .remove = __devexit_p(wm8983_i2c_remove), + .id_table = wm8983_i2c_id +}; +#endif + +static int __init wm8983_modinit(void) +{ + int ret = 0; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8983_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8983 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8983_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8983 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8983_modinit); + +static void __exit wm8983_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8983_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8983_spi_driver); +#endif +} +module_exit(wm8983_exit); + +MODULE_DESCRIPTION("ASoC WM8983 driver"); +MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8983.h b/sound/soc/codecs/wm8983.h new file mode 100644 index 000000000000..71ee619c2742 --- /dev/null +++ b/sound/soc/codecs/wm8983.h @@ -0,0 +1,1029 @@ +/* + * wm8983.h -- WM8983 ALSA SoC Audio driver + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8983_H +#define _WM8983_H + +/* + * Register values. + */ +#define WM8983_SOFTWARE_RESET 0x00 +#define WM8983_POWER_MANAGEMENT_1 0x01 +#define WM8983_POWER_MANAGEMENT_2 0x02 +#define WM8983_POWER_MANAGEMENT_3 0x03 +#define WM8983_AUDIO_INTERFACE 0x04 +#define WM8983_COMPANDING_CONTROL 0x05 +#define WM8983_CLOCK_GEN_CONTROL 0x06 +#define WM8983_ADDITIONAL_CONTROL 0x07 +#define WM8983_GPIO_CONTROL 0x08 +#define WM8983_JACK_DETECT_CONTROL_1 0x09 +#define WM8983_DAC_CONTROL 0x0A +#define WM8983_LEFT_DAC_DIGITAL_VOL 0x0B +#define WM8983_RIGHT_DAC_DIGITAL_VOL 0x0C +#define WM8983_JACK_DETECT_CONTROL_2 0x0D +#define WM8983_ADC_CONTROL 0x0E +#define WM8983_LEFT_ADC_DIGITAL_VOL 0x0F +#define WM8983_RIGHT_ADC_DIGITAL_VOL 0x10 +#define WM8983_EQ1_LOW_SHELF 0x12 +#define WM8983_EQ2_PEAK_1 0x13 +#define WM8983_EQ3_PEAK_2 0x14 +#define WM8983_EQ4_PEAK_3 0x15 +#define WM8983_EQ5_HIGH_SHELF 0x16 +#define WM8983_DAC_LIMITER_1 0x18 +#define WM8983_DAC_LIMITER_2 0x19 +#define WM8983_NOTCH_FILTER_1 0x1B +#define WM8983_NOTCH_FILTER_2 0x1C +#define WM8983_NOTCH_FILTER_3 0x1D +#define WM8983_NOTCH_FILTER_4 0x1E +#define WM8983_ALC_CONTROL_1 0x20 +#define WM8983_ALC_CONTROL_2 0x21 +#define WM8983_ALC_CONTROL_3 0x22 +#define WM8983_NOISE_GATE 0x23 +#define WM8983_PLL_N 0x24 +#define WM8983_PLL_K_1 0x25 +#define WM8983_PLL_K_2 0x26 +#define WM8983_PLL_K_3 0x27 +#define WM8983_3D_CONTROL 0x29 +#define WM8983_OUT4_TO_ADC 0x2A +#define WM8983_BEEP_CONTROL 0x2B +#define WM8983_INPUT_CTRL 0x2C +#define WM8983_LEFT_INP_PGA_GAIN_CTRL 0x2D +#define WM8983_RIGHT_INP_PGA_GAIN_CTRL 0x2E +#define WM8983_LEFT_ADC_BOOST_CTRL 0x2F +#define WM8983_RIGHT_ADC_BOOST_CTRL 0x30 +#define WM8983_OUTPUT_CTRL 0x31 +#define WM8983_LEFT_MIXER_CTRL 0x32 +#define WM8983_RIGHT_MIXER_CTRL 0x33 +#define WM8983_LOUT1_HP_VOLUME_CTRL 0x34 +#define WM8983_ROUT1_HP_VOLUME_CTRL 0x35 +#define WM8983_LOUT2_SPK_VOLUME_CTRL 0x36 +#define WM8983_ROUT2_SPK_VOLUME_CTRL 0x37 +#define WM8983_OUT3_MIXER_CTRL 0x38 +#define WM8983_OUT4_MONO_MIX_CTRL 0x39 +#define WM8983_BIAS_CTRL 0x3D + +#define WM8983_REGISTER_COUNT 59 +#define WM8983_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8983_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */ +#define WM8983_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */ +#define WM8983_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */ + +/* + * R1 (0x01) - Power management 1 + */ +#define WM8983_BUFDCOPEN 0x0100 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_MASK 0x0100 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_SHIFT 8 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8983_OUT4MIXEN 0x0080 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */ +#define WM8983_OUT3MIXEN 0x0040 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */ +#define WM8983_PLLEN 0x0020 /* PLLEN */ +#define WM8983_PLLEN_MASK 0x0020 /* PLLEN */ +#define WM8983_PLLEN_SHIFT 5 /* PLLEN */ +#define WM8983_PLLEN_WIDTH 1 /* PLLEN */ +#define WM8983_MICBEN 0x0010 /* MICBEN */ +#define WM8983_MICBEN_MASK 0x0010 /* MICBEN */ +#define WM8983_MICBEN_SHIFT 4 /* MICBEN */ +#define WM8983_MICBEN_WIDTH 1 /* MICBEN */ +#define WM8983_BIASEN 0x0008 /* BIASEN */ +#define WM8983_BIASEN_MASK 0x0008 /* BIASEN */ +#define WM8983_BIASEN_SHIFT 3 /* BIASEN */ +#define WM8983_BIASEN_WIDTH 1 /* BIASEN */ +#define WM8983_BUFIOEN 0x0004 /* BUFIOEN */ +#define WM8983_BUFIOEN_MASK 0x0004 /* BUFIOEN */ +#define WM8983_BUFIOEN_SHIFT 2 /* BUFIOEN */ +#define WM8983_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8983_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */ +#define WM8983_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */ +#define WM8983_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */ + +/* + * R2 (0x02) - Power management 2 + */ +#define WM8983_ROUT1EN 0x0100 /* ROUT1EN */ +#define WM8983_ROUT1EN_MASK 0x0100 /* ROUT1EN */ +#define WM8983_ROUT1EN_SHIFT 8 /* ROUT1EN */ +#define WM8983_ROUT1EN_WIDTH 1 /* ROUT1EN */ +#define WM8983_LOUT1EN 0x0080 /* LOUT1EN */ +#define WM8983_LOUT1EN_MASK 0x0080 /* LOUT1EN */ +#define WM8983_LOUT1EN_SHIFT 7 /* LOUT1EN */ +#define WM8983_LOUT1EN_WIDTH 1 /* LOUT1EN */ +#define WM8983_SLEEP 0x0040 /* SLEEP */ +#define WM8983_SLEEP_MASK 0x0040 /* SLEEP */ +#define WM8983_SLEEP_SHIFT 6 /* SLEEP */ +#define WM8983_SLEEP_WIDTH 1 /* SLEEP */ +#define WM8983_BOOSTENR 0x0020 /* BOOSTENR */ +#define WM8983_BOOSTENR_MASK 0x0020 /* BOOSTENR */ +#define WM8983_BOOSTENR_SHIFT 5 /* BOOSTENR */ +#define WM8983_BOOSTENR_WIDTH 1 /* BOOSTENR */ +#define WM8983_BOOSTENL 0x0010 /* BOOSTENL */ +#define WM8983_BOOSTENL_MASK 0x0010 /* BOOSTENL */ +#define WM8983_BOOSTENL_SHIFT 4 /* BOOSTENL */ +#define WM8983_BOOSTENL_WIDTH 1 /* BOOSTENL */ +#define WM8983_INPGAENR 0x0008 /* INPGAENR */ +#define WM8983_INPGAENR_MASK 0x0008 /* INPGAENR */ +#define WM8983_INPGAENR_SHIFT 3 /* INPGAENR */ +#define WM8983_INPGAENR_WIDTH 1 /* INPGAENR */ +#define WM8983_INPPGAENL 0x0004 /* INPPGAENL */ +#define WM8983_INPPGAENL_MASK 0x0004 /* INPPGAENL */ +#define WM8983_INPPGAENL_SHIFT 2 /* INPPGAENL */ +#define WM8983_INPPGAENL_WIDTH 1 /* INPPGAENL */ +#define WM8983_ADCENR 0x0002 /* ADCENR */ +#define WM8983_ADCENR_MASK 0x0002 /* ADCENR */ +#define WM8983_ADCENR_SHIFT 1 /* ADCENR */ +#define WM8983_ADCENR_WIDTH 1 /* ADCENR */ +#define WM8983_ADCENL 0x0001 /* ADCENL */ +#define WM8983_ADCENL_MASK 0x0001 /* ADCENL */ +#define WM8983_ADCENL_SHIFT 0 /* ADCENL */ +#define WM8983_ADCENL_WIDTH 1 /* ADCENL */ + +/* + * R3 (0x03) - Power management 3 + */ +#define WM8983_OUT4EN 0x0100 /* OUT4EN */ +#define WM8983_OUT4EN_MASK 0x0100 /* OUT4EN */ +#define WM8983_OUT4EN_SHIFT 8 /* OUT4EN */ +#define WM8983_OUT4EN_WIDTH 1 /* OUT4EN */ +#define WM8983_OUT3EN 0x0080 /* OUT3EN */ +#define WM8983_OUT3EN_MASK 0x0080 /* OUT3EN */ +#define WM8983_OUT3EN_SHIFT 7 /* OUT3EN */ +#define WM8983_OUT3EN_WIDTH 1 /* OUT3EN */ +#define WM8983_LOUT2EN 0x0040 /* LOUT2EN */ +#define WM8983_LOUT2EN_MASK 0x0040 /* LOUT2EN */ +#define WM8983_LOUT2EN_SHIFT 6 /* LOUT2EN */ +#define WM8983_LOUT2EN_WIDTH 1 /* LOUT2EN */ +#define WM8983_ROUT2EN 0x0020 /* ROUT2EN */ +#define WM8983_ROUT2EN_MASK 0x0020 /* ROUT2EN */ +#define WM8983_ROUT2EN_SHIFT 5 /* ROUT2EN */ +#define WM8983_ROUT2EN_WIDTH 1 /* ROUT2EN */ +#define WM8983_RMIXEN 0x0008 /* RMIXEN */ +#define WM8983_RMIXEN_MASK 0x0008 /* RMIXEN */ +#define WM8983_RMIXEN_SHIFT 3 /* RMIXEN */ +#define WM8983_RMIXEN_WIDTH 1 /* RMIXEN */ +#define WM8983_LMIXEN 0x0004 /* LMIXEN */ +#define WM8983_LMIXEN_MASK 0x0004 /* LMIXEN */ +#define WM8983_LMIXEN_SHIFT 2 /* LMIXEN */ +#define WM8983_LMIXEN_WIDTH 1 /* LMIXEN */ +#define WM8983_DACENR 0x0002 /* DACENR */ +#define WM8983_DACENR_MASK 0x0002 /* DACENR */ +#define WM8983_DACENR_SHIFT 1 /* DACENR */ +#define WM8983_DACENR_WIDTH 1 /* DACENR */ +#define WM8983_DACENL 0x0001 /* DACENL */ +#define WM8983_DACENL_MASK 0x0001 /* DACENL */ +#define WM8983_DACENL_SHIFT 0 /* DACENL */ +#define WM8983_DACENL_WIDTH 1 /* DACENL */ + +/* + * R4 (0x04) - Audio Interface + */ +#define WM8983_BCP 0x0100 /* BCP */ +#define WM8983_BCP_MASK 0x0100 /* BCP */ +#define WM8983_BCP_SHIFT 8 /* BCP */ +#define WM8983_BCP_WIDTH 1 /* BCP */ +#define WM8983_LRCP 0x0080 /* LRCP */ +#define WM8983_LRCP_MASK 0x0080 /* LRCP */ +#define WM8983_LRCP_SHIFT 7 /* LRCP */ +#define WM8983_LRCP_WIDTH 1 /* LRCP */ +#define WM8983_WL_MASK 0x0060 /* WL - [6:5] */ +#define WM8983_WL_SHIFT 5 /* WL - [6:5] */ +#define WM8983_WL_WIDTH 2 /* WL - [6:5] */ +#define WM8983_FMT_MASK 0x0018 /* FMT - [4:3] */ +#define WM8983_FMT_SHIFT 3 /* FMT - [4:3] */ +#define WM8983_FMT_WIDTH 2 /* FMT - [4:3] */ +#define WM8983_DLRSWAP 0x0004 /* DLRSWAP */ +#define WM8983_DLRSWAP_MASK 0x0004 /* DLRSWAP */ +#define WM8983_DLRSWAP_SHIFT 2 /* DLRSWAP */ +#define WM8983_DLRSWAP_WIDTH 1 /* DLRSWAP */ +#define WM8983_ALRSWAP 0x0002 /* ALRSWAP */ +#define WM8983_ALRSWAP_MASK 0x0002 /* ALRSWAP */ +#define WM8983_ALRSWAP_SHIFT 1 /* ALRSWAP */ +#define WM8983_ALRSWAP_WIDTH 1 /* ALRSWAP */ +#define WM8983_MONO 0x0001 /* MONO */ +#define WM8983_MONO_MASK 0x0001 /* MONO */ +#define WM8983_MONO_SHIFT 0 /* MONO */ +#define WM8983_MONO_WIDTH 1 /* MONO */ + +/* + * R5 (0x05) - Companding control + */ +#define WM8983_WL8 0x0020 /* WL8 */ +#define WM8983_WL8_MASK 0x0020 /* WL8 */ +#define WM8983_WL8_SHIFT 5 /* WL8 */ +#define WM8983_WL8_WIDTH 1 /* WL8 */ +#define WM8983_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */ +#define WM8983_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */ +#define WM8983_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */ +#define WM8983_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */ +#define WM8983_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */ +#define WM8983_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */ +#define WM8983_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8983_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8983_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8983_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R6 (0x06) - Clock Gen control + */ +#define WM8983_CLKSEL 0x0100 /* CLKSEL */ +#define WM8983_CLKSEL_MASK 0x0100 /* CLKSEL */ +#define WM8983_CLKSEL_SHIFT 8 /* CLKSEL */ +#define WM8983_CLKSEL_WIDTH 1 /* CLKSEL */ +#define WM8983_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */ +#define WM8983_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */ +#define WM8983_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */ +#define WM8983_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */ +#define WM8983_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */ +#define WM8983_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */ +#define WM8983_MS 0x0001 /* MS */ +#define WM8983_MS_MASK 0x0001 /* MS */ +#define WM8983_MS_SHIFT 0 /* MS */ +#define WM8983_MS_WIDTH 1 /* MS */ + +/* + * R7 (0x07) - Additional control + */ +#define WM8983_SR_MASK 0x000E /* SR - [3:1] */ +#define WM8983_SR_SHIFT 1 /* SR - [3:1] */ +#define WM8983_SR_WIDTH 3 /* SR - [3:1] */ +#define WM8983_SLOWCLKEN 0x0001 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */ + +/* + * R8 (0x08) - GPIO Control + */ +#define WM8983_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [5:4] */ +#define WM8983_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [5:4] */ +#define WM8983_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [5:4] */ +#define WM8983_GPIO1POL 0x0008 /* GPIO1POL */ +#define WM8983_GPIO1POL_MASK 0x0008 /* GPIO1POL */ +#define WM8983_GPIO1POL_SHIFT 3 /* GPIO1POL */ +#define WM8983_GPIO1POL_WIDTH 1 /* GPIO1POL */ +#define WM8983_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */ +#define WM8983_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */ +#define WM8983_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */ + +/* + * R9 (0x09) - Jack Detect Control 1 + */ +#define WM8983_JD_VMID1 0x0100 /* JD_VMID1 */ +#define WM8983_JD_VMID1_MASK 0x0100 /* JD_VMID1 */ +#define WM8983_JD_VMID1_SHIFT 8 /* JD_VMID1 */ +#define WM8983_JD_VMID1_WIDTH 1 /* JD_VMID1 */ +#define WM8983_JD_VMID0 0x0080 /* JD_VMID0 */ +#define WM8983_JD_VMID0_MASK 0x0080 /* JD_VMID0 */ +#define WM8983_JD_VMID0_SHIFT 7 /* JD_VMID0 */ +#define WM8983_JD_VMID0_WIDTH 1 /* JD_VMID0 */ +#define WM8983_JD_EN 0x0040 /* JD_EN */ +#define WM8983_JD_EN_MASK 0x0040 /* JD_EN */ +#define WM8983_JD_EN_SHIFT 6 /* JD_EN */ +#define WM8983_JD_EN_WIDTH 1 /* JD_EN */ +#define WM8983_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */ +#define WM8983_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */ +#define WM8983_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */ + +/* + * R10 (0x0A) - DAC Control + */ +#define WM8983_SOFTMUTE 0x0040 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_SHIFT 6 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_WIDTH 1 /* SOFTMUTE */ +#define WM8983_DACOSR128 0x0008 /* DACOSR128 */ +#define WM8983_DACOSR128_MASK 0x0008 /* DACOSR128 */ +#define WM8983_DACOSR128_SHIFT 3 /* DACOSR128 */ +#define WM8983_DACOSR128_WIDTH 1 /* DACOSR128 */ +#define WM8983_AMUTE 0x0004 /* AMUTE */ +#define WM8983_AMUTE_MASK 0x0004 /* AMUTE */ +#define WM8983_AMUTE_SHIFT 2 /* AMUTE */ +#define WM8983_AMUTE_WIDTH 1 /* AMUTE */ +#define WM8983_DACRPOL 0x0002 /* DACRPOL */ +#define WM8983_DACRPOL_MASK 0x0002 /* DACRPOL */ +#define WM8983_DACRPOL_SHIFT 1 /* DACRPOL */ +#define WM8983_DACRPOL_WIDTH 1 /* DACRPOL */ +#define WM8983_DACLPOL 0x0001 /* DACLPOL */ +#define WM8983_DACLPOL_MASK 0x0001 /* DACLPOL */ +#define WM8983_DACLPOL_SHIFT 0 /* DACLPOL */ +#define WM8983_DACLPOL_WIDTH 1 /* DACLPOL */ + +/* + * R11 (0x0B) - Left DAC digital Vol + */ +#define WM8983_DACVU 0x0100 /* DACVU */ +#define WM8983_DACVU_MASK 0x0100 /* DACVU */ +#define WM8983_DACVU_SHIFT 8 /* DACVU */ +#define WM8983_DACVU_WIDTH 1 /* DACVU */ +#define WM8983_DACLVOL_MASK 0x00FF /* DACLVOL - [7:0] */ +#define WM8983_DACLVOL_SHIFT 0 /* DACLVOL - [7:0] */ +#define WM8983_DACLVOL_WIDTH 8 /* DACLVOL - [7:0] */ + +/* + * R12 (0x0C) - Right DAC digital vol + */ +#define WM8983_DACVU 0x0100 /* DACVU */ +#define WM8983_DACVU_MASK 0x0100 /* DACVU */ +#define WM8983_DACVU_SHIFT 8 /* DACVU */ +#define WM8983_DACVU_WIDTH 1 /* DACVU */ +#define WM8983_DACRVOL_MASK 0x00FF /* DACRVOL - [7:0] */ +#define WM8983_DACRVOL_SHIFT 0 /* DACRVOL - [7:0] */ +#define WM8983_DACRVOL_WIDTH 8 /* DACRVOL - [7:0] */ + +/* + * R13 (0x0D) - Jack Detect Control 2 + */ +#define WM8983_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */ +#define WM8983_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */ +#define WM8983_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */ + +/* + * R14 (0x0E) - ADC Control + */ +#define WM8983_HPFEN 0x0100 /* HPFEN */ +#define WM8983_HPFEN_MASK 0x0100 /* HPFEN */ +#define WM8983_HPFEN_SHIFT 8 /* HPFEN */ +#define WM8983_HPFEN_WIDTH 1 /* HPFEN */ +#define WM8983_HPFAPP 0x0080 /* HPFAPP */ +#define WM8983_HPFAPP_MASK 0x0080 /* HPFAPP */ +#define WM8983_HPFAPP_SHIFT 7 /* HPFAPP */ +#define WM8983_HPFAPP_WIDTH 1 /* HPFAPP */ +#define WM8983_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */ +#define WM8983_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */ +#define WM8983_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */ +#define WM8983_ADCOSR128 0x0008 /* ADCOSR128 */ +#define WM8983_ADCOSR128_MASK 0x0008 /* ADCOSR128 */ +#define WM8983_ADCOSR128_SHIFT 3 /* ADCOSR128 */ +#define WM8983_ADCOSR128_WIDTH 1 /* ADCOSR128 */ +#define WM8983_ADCRPOL 0x0002 /* ADCRPOL */ +#define WM8983_ADCRPOL_MASK 0x0002 /* ADCRPOL */ +#define WM8983_ADCRPOL_SHIFT 1 /* ADCRPOL */ +#define WM8983_ADCRPOL_WIDTH 1 /* ADCRPOL */ +#define WM8983_ADCLPOL 0x0001 /* ADCLPOL */ +#define WM8983_ADCLPOL_MASK 0x0001 /* ADCLPOL */ +#define WM8983_ADCLPOL_SHIFT 0 /* ADCLPOL */ +#define WM8983_ADCLPOL_WIDTH 1 /* ADCLPOL */ + +/* + * R15 (0x0F) - Left ADC Digital Vol + */ +#define WM8983_ADCVU 0x0100 /* ADCVU */ +#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8983_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8983_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8983_ADCLVOL_MASK 0x00FF /* ADCLVOL - [7:0] */ +#define WM8983_ADCLVOL_SHIFT 0 /* ADCLVOL - [7:0] */ +#define WM8983_ADCLVOL_WIDTH 8 /* ADCLVOL - [7:0] */ + +/* + * R16 (0x10) - Right ADC Digital Vol + */ +#define WM8983_ADCVU 0x0100 /* ADCVU */ +#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8983_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8983_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8983_ADCRVOL_MASK 0x00FF /* ADCRVOL - [7:0] */ +#define WM8983_ADCRVOL_SHIFT 0 /* ADCRVOL - [7:0] */ +#define WM8983_ADCRVOL_WIDTH 8 /* ADCRVOL - [7:0] */ + +/* + * R18 (0x12) - EQ1 - low shelf + */ +#define WM8983_EQ3DMODE 0x0100 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_SHIFT 8 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_WIDTH 1 /* EQ3DMODE */ +#define WM8983_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */ +#define WM8983_EQ1C_SHIFT 5 /* EQ1C - [6:5] */ +#define WM8983_EQ1C_WIDTH 2 /* EQ1C - [6:5] */ +#define WM8983_EQ1G_MASK 0x001F /* EQ1G - [4:0] */ +#define WM8983_EQ1G_SHIFT 0 /* EQ1G - [4:0] */ +#define WM8983_EQ1G_WIDTH 5 /* EQ1G - [4:0] */ + +/* + * R19 (0x13) - EQ2 - peak 1 + */ +#define WM8983_EQ2BW 0x0100 /* EQ2BW */ +#define WM8983_EQ2BW_MASK 0x0100 /* EQ2BW */ +#define WM8983_EQ2BW_SHIFT 8 /* EQ2BW */ +#define WM8983_EQ2BW_WIDTH 1 /* EQ2BW */ +#define WM8983_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */ +#define WM8983_EQ2C_SHIFT 5 /* EQ2C - [6:5] */ +#define WM8983_EQ2C_WIDTH 2 /* EQ2C - [6:5] */ +#define WM8983_EQ2G_MASK 0x001F /* EQ2G - [4:0] */ +#define WM8983_EQ2G_SHIFT 0 /* EQ2G - [4:0] */ +#define WM8983_EQ2G_WIDTH 5 /* EQ2G - [4:0] */ + +/* + * R20 (0x14) - EQ3 - peak 2 + */ +#define WM8983_EQ3BW 0x0100 /* EQ3BW */ +#define WM8983_EQ3BW_MASK 0x0100 /* EQ3BW */ +#define WM8983_EQ3BW_SHIFT 8 /* EQ3BW */ +#define WM8983_EQ3BW_WIDTH 1 /* EQ3BW */ +#define WM8983_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */ +#define WM8983_EQ3C_SHIFT 5 /* EQ3C - [6:5] */ +#define WM8983_EQ3C_WIDTH 2 /* EQ3C - [6:5] */ +#define WM8983_EQ3G_MASK 0x001F /* EQ3G - [4:0] */ +#define WM8983_EQ3G_SHIFT 0 /* EQ3G - [4:0] */ +#define WM8983_EQ3G_WIDTH 5 /* EQ3G - [4:0] */ + +/* + * R21 (0x15) - EQ4 - peak 3 + */ +#define WM8983_EQ4BW 0x0100 /* EQ4BW */ +#define WM8983_EQ4BW_MASK 0x0100 /* EQ4BW */ +#define WM8983_EQ4BW_SHIFT 8 /* EQ4BW */ +#define WM8983_EQ4BW_WIDTH 1 /* EQ4BW */ +#define WM8983_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */ +#define WM8983_EQ4C_SHIFT 5 /* EQ4C - [6:5] */ +#define WM8983_EQ4C_WIDTH 2 /* EQ4C - [6:5] */ +#define WM8983_EQ4G_MASK 0x001F /* EQ4G - [4:0] */ +#define WM8983_EQ4G_SHIFT 0 /* EQ4G - [4:0] */ +#define WM8983_EQ4G_WIDTH 5 /* EQ4G - [4:0] */ + +/* + * R22 (0x16) - EQ5 - high shelf + */ +#define WM8983_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */ +#define WM8983_EQ5C_SHIFT 5 /* EQ5C - [6:5] */ +#define WM8983_EQ5C_WIDTH 2 /* EQ5C - [6:5] */ +#define WM8983_EQ5G_MASK 0x001F /* EQ5G - [4:0] */ +#define WM8983_EQ5G_SHIFT 0 /* EQ5G - [4:0] */ +#define WM8983_EQ5G_WIDTH 5 /* EQ5G - [4:0] */ + +/* + * R24 (0x18) - DAC Limiter 1 + */ +#define WM8983_LIMEN 0x0100 /* LIMEN */ +#define WM8983_LIMEN_MASK 0x0100 /* LIMEN */ +#define WM8983_LIMEN_SHIFT 8 /* LIMEN */ +#define WM8983_LIMEN_WIDTH 1 /* LIMEN */ +#define WM8983_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */ +#define WM8983_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */ +#define WM8983_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */ +#define WM8983_LIMATK_MASK 0x000F /* LIMATK - [3:0] */ +#define WM8983_LIMATK_SHIFT 0 /* LIMATK - [3:0] */ +#define WM8983_LIMATK_WIDTH 4 /* LIMATK - [3:0] */ + +/* + * R25 (0x19) - DAC Limiter 2 + */ +#define WM8983_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */ +#define WM8983_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */ +#define WM8983_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */ +#define WM8983_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */ +#define WM8983_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */ +#define WM8983_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */ + +/* + * R27 (0x1B) - Notch Filter 1 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFEN 0x0080 /* NFEN */ +#define WM8983_NFEN_MASK 0x0080 /* NFEN */ +#define WM8983_NFEN_SHIFT 7 /* NFEN */ +#define WM8983_NFEN_WIDTH 1 /* NFEN */ +#define WM8983_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */ +#define WM8983_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */ +#define WM8983_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */ + +/* + * R28 (0x1C) - Notch Filter 2 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */ +#define WM8983_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */ +#define WM8983_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */ + +/* + * R29 (0x1D) - Notch Filter 3 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */ +#define WM8983_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */ +#define WM8983_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */ + +/* + * R30 (0x1E) - Notch Filter 4 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */ +#define WM8983_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */ +#define WM8983_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */ + +/* + * R32 (0x20) - ALC control 1 + */ +#define WM8983_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8983_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8983_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8983_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */ +#define WM8983_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */ +#define WM8983_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */ +#define WM8983_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */ +#define WM8983_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */ +#define WM8983_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */ + +/* + * R33 (0x21) - ALC control 2 + */ +#define WM8983_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */ +#define WM8983_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */ +#define WM8983_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */ +#define WM8983_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */ +#define WM8983_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */ +#define WM8983_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */ + +/* + * R34 (0x22) - ALC control 3 + */ +#define WM8983_ALCMODE 0x0100 /* ALCMODE */ +#define WM8983_ALCMODE_MASK 0x0100 /* ALCMODE */ +#define WM8983_ALCMODE_SHIFT 8 /* ALCMODE */ +#define WM8983_ALCMODE_WIDTH 1 /* ALCMODE */ +#define WM8983_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */ +#define WM8983_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */ +#define WM8983_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */ +#define WM8983_ALCATK_MASK 0x000F /* ALCATK - [3:0] */ +#define WM8983_ALCATK_SHIFT 0 /* ALCATK - [3:0] */ +#define WM8983_ALCATK_WIDTH 4 /* ALCATK - [3:0] */ + +/* + * R35 (0x23) - Noise Gate + */ +#define WM8983_NGEN 0x0008 /* NGEN */ +#define WM8983_NGEN_MASK 0x0008 /* NGEN */ +#define WM8983_NGEN_SHIFT 3 /* NGEN */ +#define WM8983_NGEN_WIDTH 1 /* NGEN */ +#define WM8983_NGTH_MASK 0x0007 /* NGTH - [2:0] */ +#define WM8983_NGTH_SHIFT 0 /* NGTH - [2:0] */ +#define WM8983_NGTH_WIDTH 3 /* NGTH - [2:0] */ + +/* + * R36 (0x24) - PLL N + */ +#define WM8983_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */ +#define WM8983_PLLN_MASK 0x000F /* PLLN - [3:0] */ +#define WM8983_PLLN_SHIFT 0 /* PLLN - [3:0] */ +#define WM8983_PLLN_WIDTH 4 /* PLLN - [3:0] */ + +/* + * R37 (0x25) - PLL K 1 + */ +#define WM8983_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */ +#define WM8983_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */ +#define WM8983_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */ + +/* + * R38 (0x26) - PLL K 2 + */ +#define WM8983_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */ +#define WM8983_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */ +#define WM8983_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */ + +/* + * R39 (0x27) - PLL K 3 + */ +#define WM8983_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */ +#define WM8983_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */ +#define WM8983_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */ + +/* + * R41 (0x29) - 3D control + */ +#define WM8983_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */ +#define WM8983_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */ +#define WM8983_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */ + +/* + * R42 (0x2A) - OUT4 to ADC + */ +#define WM8983_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2LNR 0x0020 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */ +#define WM8983_POBCTRL 0x0004 /* POBCTRL */ +#define WM8983_POBCTRL_MASK 0x0004 /* POBCTRL */ +#define WM8983_POBCTRL_SHIFT 2 /* POBCTRL */ +#define WM8983_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8983_DELEN 0x0002 /* DELEN */ +#define WM8983_DELEN_MASK 0x0002 /* DELEN */ +#define WM8983_DELEN_SHIFT 1 /* DELEN */ +#define WM8983_DELEN_WIDTH 1 /* DELEN */ +#define WM8983_OUT1DEL 0x0001 /* OUT1DEL */ +#define WM8983_OUT1DEL_MASK 0x0001 /* OUT1DEL */ +#define WM8983_OUT1DEL_SHIFT 0 /* OUT1DEL */ +#define WM8983_OUT1DEL_WIDTH 1 /* OUT1DEL */ + +/* + * R43 (0x2B) - Beep control + */ +#define WM8983_BYPL2RMIX 0x0100 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */ +#define WM8983_BYPR2LMIX 0x0080 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */ +#define WM8983_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */ +#define WM8983_INVROUT2 0x0010 /* INVROUT2 */ +#define WM8983_INVROUT2_MASK 0x0010 /* INVROUT2 */ +#define WM8983_INVROUT2_SHIFT 4 /* INVROUT2 */ +#define WM8983_INVROUT2_WIDTH 1 /* INVROUT2 */ +#define WM8983_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */ +#define WM8983_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */ +#define WM8983_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */ +#define WM8983_BEEPEN 0x0001 /* BEEPEN */ +#define WM8983_BEEPEN_MASK 0x0001 /* BEEPEN */ +#define WM8983_BEEPEN_SHIFT 0 /* BEEPEN */ +#define WM8983_BEEPEN_WIDTH 1 /* BEEPEN */ + +/* + * R44 (0x2C) - Input ctrl + */ +#define WM8983_MBVSEL 0x0100 /* MBVSEL */ +#define WM8983_MBVSEL_MASK 0x0100 /* MBVSEL */ +#define WM8983_MBVSEL_SHIFT 8 /* MBVSEL */ +#define WM8983_MBVSEL_WIDTH 1 /* MBVSEL */ +#define WM8983_R2_2INPPGA 0x0040 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */ +#define WM8983_RIN2INPPGA 0x0020 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */ +#define WM8983_RIP2INPPGA 0x0010 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */ +#define WM8983_L2_2INPPGA 0x0004 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */ +#define WM8983_LIN2INPPGA 0x0002 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */ +#define WM8983_LIP2INPPGA 0x0001 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */ + +/* + * R45 (0x2D) - Left INP PGA gain ctrl + */ +#define WM8983_INPGAVU 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8983_INPPGAZCL 0x0080 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_SHIFT 7 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_WIDTH 1 /* INPPGAZCL */ +#define WM8983_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */ +#define WM8983_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */ +#define WM8983_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */ +#define WM8983_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */ + +/* + * R46 (0x2E) - Right INP PGA gain ctrl + */ +#define WM8983_INPGAVU 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8983_INPPGAZCR 0x0080 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_SHIFT 7 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_WIDTH 1 /* INPPGAZCR */ +#define WM8983_INPPGAMUTER 0x0040 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */ +#define WM8983_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */ +#define WM8983_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */ +#define WM8983_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */ + +/* + * R47 (0x2F) - Left ADC BOOST ctrl + */ +#define WM8983_PGABOOSTL 0x0100 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_SHIFT 8 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_WIDTH 1 /* PGABOOSTL */ +#define WM8983_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8983_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8983_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */ + +/* + * R48 (0x30) - Right ADC BOOST ctrl + */ +#define WM8983_PGABOOSTR 0x0100 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_SHIFT 8 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_WIDTH 1 /* PGABOOSTR */ +#define WM8983_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8983_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8983_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */ + +/* + * R49 (0x31) - Output ctrl + */ +#define WM8983_DACL2RMIX 0x0040 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_SHIFT 6 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_WIDTH 1 /* DACL2RMIX */ +#define WM8983_DACR2LMIX 0x0020 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_SHIFT 5 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_WIDTH 1 /* DACR2LMIX */ +#define WM8983_OUT4BOOST 0x0010 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_SHIFT 4 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_WIDTH 1 /* OUT4BOOST */ +#define WM8983_OUT3BOOST 0x0008 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_SHIFT 3 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_WIDTH 1 /* OUT3BOOST */ +#define WM8983_SPKBOOST 0x0004 /* SPKBOOST */ +#define WM8983_SPKBOOST_MASK 0x0004 /* SPKBOOST */ +#define WM8983_SPKBOOST_SHIFT 2 /* SPKBOOST */ +#define WM8983_SPKBOOST_WIDTH 1 /* SPKBOOST */ +#define WM8983_TSDEN 0x0002 /* TSDEN */ +#define WM8983_TSDEN_MASK 0x0002 /* TSDEN */ +#define WM8983_TSDEN_SHIFT 1 /* TSDEN */ +#define WM8983_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8983_VROI 0x0001 /* VROI */ +#define WM8983_VROI_MASK 0x0001 /* VROI */ +#define WM8983_VROI_SHIFT 0 /* VROI */ +#define WM8983_VROI_WIDTH 1 /* VROI */ + +/* + * R50 (0x32) - Left mixer ctrl + */ +#define WM8983_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXL2LMIX 0x0020 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */ +#define WM8983_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPL2LMIX 0x0002 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */ +#define WM8983_DACL2LMIX 0x0001 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_SHIFT 0 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_WIDTH 1 /* DACL2LMIX */ + +/* + * R51 (0x33) - Right mixer ctrl + */ +#define WM8983_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXR2RMIX 0x0020 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */ +#define WM8983_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPR2RMIX 0x0002 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */ +#define WM8983_DACR2RMIX 0x0001 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_SHIFT 0 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_WIDTH 1 /* DACR2RMIX */ + +/* + * R52 (0x34) - LOUT1 (HP) volume ctrl + */ +#define WM8983_OUT1VU 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8983_LOUT1ZC 0x0080 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_SHIFT 7 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_WIDTH 1 /* LOUT1ZC */ +#define WM8983_LOUT1MUTE 0x0040 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */ +#define WM8983_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */ +#define WM8983_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */ +#define WM8983_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */ + +/* + * R53 (0x35) - ROUT1 (HP) volume ctrl + */ +#define WM8983_OUT1VU 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8983_ROUT1ZC 0x0080 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_SHIFT 7 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_WIDTH 1 /* ROUT1ZC */ +#define WM8983_ROUT1MUTE 0x0040 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */ +#define WM8983_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */ +#define WM8983_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */ +#define WM8983_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */ + +/* + * R54 (0x36) - LOUT2 (SPK) volume ctrl + */ +#define WM8983_OUT2VU 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8983_LOUT2ZC 0x0080 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_SHIFT 7 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_WIDTH 1 /* LOUT2ZC */ +#define WM8983_LOUT2MUTE 0x0040 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */ +#define WM8983_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */ +#define WM8983_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */ +#define WM8983_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */ + +/* + * R55 (0x37) - ROUT2 (SPK) volume ctrl + */ +#define WM8983_OUT2VU 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8983_ROUT2ZC 0x0080 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_SHIFT 7 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_WIDTH 1 /* ROUT2ZC */ +#define WM8983_ROUT2MUTE 0x0040 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */ +#define WM8983_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */ +#define WM8983_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */ +#define WM8983_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */ + +/* + * R56 (0x38) - OUT3 mixer ctrl + */ +#define WM8983_OUT3MUTE 0x0040 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_SHIFT 6 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_WIDTH 1 /* OUT3MUTE */ +#define WM8983_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */ +#define WM8983_BYPL2OUT3 0x0004 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */ +#define WM8983_LMIX2OUT3 0x0002 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */ +#define WM8983_LDAC2OUT3 0x0001 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */ + +/* + * R57 (0x39) - OUT4 (MONO) mix ctrl + */ +#define WM8983_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */ +#define WM8983_OUT4MUTE 0x0040 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_SHIFT 6 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_WIDTH 1 /* OUT4MUTE */ +#define WM8983_OUT4ATTN 0x0020 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_SHIFT 5 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_WIDTH 1 /* OUT4ATTN */ +#define WM8983_LMIX2OUT4 0x0010 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */ +#define WM8983_LDAC2OUT4 0x0008 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */ +#define WM8983_BYPR2OUT4 0x0004 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */ +#define WM8983_RMIX2OUT4 0x0002 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */ +#define WM8983_RDAC2OUT4 0x0001 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */ + +/* + * R61 (0x3D) - BIAS CTRL + */ +#define WM8983_BIASCUT 0x0100 /* BIASCUT */ +#define WM8983_BIASCUT_MASK 0x0100 /* BIASCUT */ +#define WM8983_BIASCUT_SHIFT 8 /* BIASCUT */ +#define WM8983_BIASCUT_WIDTH 1 /* BIASCUT */ +#define WM8983_HALFIPBIAS 0x0080 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */ +#define WM8983_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */ +#define WM8983_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */ +#define WM8983_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */ +#define WM8983_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */ +#define WM8983_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */ +#define WM8983_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */ +#define WM8983_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */ +#define WM8983_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */ +#define WM8983_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */ +#define WM8983_HALFOPBIAS 0x0001 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */ + +enum clk_src { + WM8983_CLKSRC_MCLK, + WM8983_CLKSRC_PLL +}; + +#endif /* _WM8983_H */ diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 9e5ff789b805..6e85b8869af7 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -876,7 +876,7 @@ SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), - +SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), }; static const struct snd_soc_dapm_route routes[] = { @@ -1434,6 +1434,7 @@ static int wm8993_probe(struct snd_soc_codec *codec) wm8993->hubs_data.hp_startup_mode = 1; wm8993->hubs_data.dcs_codes = -2; + wm8993->hubs_data.series_startup = 1; ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); if (ret != 0) { diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 83014a7c2e14..09e680ae88b2 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -195,10 +195,6 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif) aif + 1, rate); } - if (rate && rate < 3000000) - dev_warn(codec->dev, "AIF%dCLK is %dHz, should be >=3MHz for optimal performance\n", - aif + 1, rate); - wm8994->aifclk[aif] = rate; snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset, @@ -1146,13 +1142,33 @@ SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, late_enable_ev, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer), + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer), + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) }; static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0) +SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), }; static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { @@ -1282,14 +1298,6 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), -SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), - -SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, - left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), -SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, - right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), - SND_SOC_DAPM_POST("Debug log", post_ev), }; @@ -1624,6 +1632,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, int reg_offset, ret; struct fll_div fll; u16 reg, aif1, aif2; + unsigned long timeout; aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1) & WM8994_AIF1CLK_ENA; @@ -1705,6 +1714,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) | (src - 1)); + /* Clear any pending completion from a previous failure */ + try_wait_for_completion(&wm8994->fll_locked[id]); + /* Enable (with fractional mode if required) */ if (freq_out) { if (fll.k) @@ -1715,7 +1727,15 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, WM8994_FLL1_ENA | WM8994_FLL1_FRAC, reg); - msleep(5); + if (wm8994->fll_locked_irq) { + timeout = wait_for_completion_timeout(&wm8994->fll_locked[id], + msecs_to_jiffies(10)); + if (timeout == 0) + dev_warn(codec->dev, + "Timed out waiting for FLL lock\n"); + } else { + msleep(5); + } } wm8994->fll[id].in = freq_in; @@ -1733,6 +1753,14 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, return 0; } +static irqreturn_t wm8994_fll_locked_irq(int irq, void *data) +{ + struct completion *completion = data; + + complete(completion); + + return IRQ_HANDLED; +} static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; @@ -2272,6 +2300,33 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); } +static void wm8994_aif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_reg = 0; + + switch (dai->id) { + case 1: + rate_reg = WM8994_AIF1_RATE; + break; + case 2: + rate_reg = WM8994_AIF1_RATE; + break; + default: + break; + } + + /* If the DAI is idle then configure the divider tree for the + * lowest output rate to save a little power if the clock is + * still active (eg, because it is system clock). + */ + if (rate_reg && !dai->playback_active && !dai->capture_active) + snd_soc_update_bits(codec, rate_reg, + WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, 0x9); +} + static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; @@ -2338,6 +2393,7 @@ static struct snd_soc_dai_ops wm8994_aif1_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, + .shutdown = wm8994_aif_shutdown, .digital_mute = wm8994_aif_mute, .set_pll = wm8994_set_fll, .set_tristate = wm8994_set_tristate, @@ -2347,6 +2403,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, + .shutdown = wm8994_aif_shutdown, .digital_mute = wm8994_aif_mute, .set_pll = wm8994_set_fll, .set_tristate = wm8994_set_tristate, @@ -2850,6 +2907,15 @@ out: return IRQ_HANDLED; } +static irqreturn_t wm8994_fifo_error(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + + dev_err(codec->dev, "FIFO error\n"); + + return IRQ_HANDLED; +} + static int wm8994_codec_probe(struct snd_soc_codec *codec) { struct wm8994 *control; @@ -2868,6 +2934,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + init_completion(&wm8994->fll_locked[i]); + if (wm8994->pdata && wm8994->pdata->micdet_irq) wm8994->micdet_irq = wm8994->pdata->micdet_irq; else if (wm8994->pdata && wm8994->pdata->irq_base) @@ -2906,6 +2975,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->hubs.dcs_codes = -5; wm8994->hubs.hp_startup_mode = 1; wm8994->hubs.dcs_readback_mode = 1; + wm8994->hubs.series_startup = 1; break; default: wm8994->hubs.dcs_readback_mode = 1; @@ -2920,6 +2990,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } + wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, + wm8994_fifo_error, "FIFO error", codec); + + ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + wm_hubs_dcs_done, "DC servo done", + &wm8994->hubs); + if (ret == 0) + wm8994->hubs.dcs_done_irq = true; + switch (control->type) { case WM8994: if (wm8994->micdet_irq) { @@ -2976,6 +3055,16 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } } + wm8994->fll_locked_irq = true; + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) { + ret = wm8994_request_irq(codec->control_data, + WM8994_IRQ_FLL1_LOCK + i, + wm8994_fll_locked_irq, "FLL lock", + &wm8994->fll_locked[i]); + if (ret != 0) + wm8994->fll_locked_irq = false; + } + /* Remember if AIFnLRCLK is configured as a GPIO. This should be * configured on init - if a system wants to do this dynamically * at runtime we can deal with that then. @@ -3051,10 +3140,18 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT, 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT); - /* Unconditionally enable AIF1 ADC TDM mode; it only affects - * behaviour on idle TDM clock cycles. */ - snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1, - WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM); + /* Unconditionally enable AIF1 ADC TDM mode on chips which can + * use this; it only affects behaviour on idle TDM clock + * cycles. */ + switch (control->type) { + case WM8994: + case WM8958: + snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1, + WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM); + break; + default: + break; + } wm8994_update_class_w(codec); @@ -3153,6 +3250,12 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); if (wm8994->micdet_irq) free_irq(wm8994->micdet_irq, wm8994); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i, + &wm8994->fll_locked[i]); + wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + &wm8994->hubs); + wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec); err: kfree(wm8994); return ret; @@ -3162,11 +3265,20 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = codec->control_data; + int i; wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); pm_runtime_disable(codec->dev); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i, + &wm8994->fll_locked[i]); + + wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + &wm8994->hubs); + wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec); + switch (control->type) { case WM8994: if (wm8994->micdet_irq) diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 0a1db04b73bd..1ab2266039f7 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -11,6 +11,7 @@ #include <sound/soc.h> #include <linux/firmware.h> +#include <linux/completion.h> #include "wm_hubs.h" @@ -79,6 +80,8 @@ struct wm8994_priv { int mclk[2]; int aifclk[2]; struct wm8994_fll_config fll[2], fll_suspend[2]; + struct completion fll_locked[2]; + bool fll_locked_irq; int dac_rates[2]; int lrclk_shared[2]; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 91c6b39de50c..a4691321f9b3 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -727,7 +727,7 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0), SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0), -SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LINEOUT"), SND_SOC_DAPM_OUTPUT("SPKN"), diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 9e370d14ad88..4cc2d567f22f 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -63,8 +63,10 @@ static const struct soc_enum speaker_mode = static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) { + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); unsigned int reg; int count = 0; + int timeout; unsigned int val; val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1; @@ -74,18 +76,39 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) dev_dbg(codec->dev, "Waiting for DC servo...\n"); + if (hubs->dcs_done_irq) + timeout = 4; + else + timeout = 400; + do { count++; - msleep(1); + + if (hubs->dcs_done_irq) + wait_for_completion_timeout(&hubs->dcs_done, + msecs_to_jiffies(250)); + else + msleep(1); + reg = snd_soc_read(codec, WM8993_DC_SERVO_0); dev_dbg(codec->dev, "DC servo: %x\n", reg); - } while (reg & op && count < 400); + } while (reg & op && count < timeout); if (reg & op) dev_err(codec->dev, "Timed out waiting for DC Servo %x\n", op); } +irqreturn_t wm_hubs_dcs_done(int irq, void *data) +{ + struct wm_hubs_data *hubs = data; + + complete(&hubs->dcs_done); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_hubs_dcs_done); + /* * Startup calibration of the DC servo */ @@ -107,8 +130,7 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) return; } - /* Devices not using a DCS code correction have startup mode */ - if (hubs->dcs_codes) { + if (hubs->series_startup) { /* Set for 32 series updates */ snd_soc_update_bits(codec, WM8993_DC_SERVO_1, WM8993_DCS_SERIES_NO_01_MASK, @@ -134,9 +156,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) break; case 1: reg = snd_soc_read(codec, WM8993_DC_SERVO_3); - reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) + reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; - reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; + reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; break; default: WARN(1, "Unknown DCS readback method\n"); @@ -150,13 +172,13 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Applying %d code DC servo correction\n", hubs->dcs_codes); - /* HPOUT1L */ - offset = reg_l; + /* HPOUT1R */ + offset = reg_r; offset += hubs->dcs_codes; dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; - /* HPOUT1R */ - offset = reg_r; + /* HPOUT1L */ + offset = reg_l; offset += hubs->dcs_codes; dcs_cfg |= (u8)offset; @@ -168,8 +190,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) WM8993_DCS_TRIG_DAC_WR_0 | WM8993_DCS_TRIG_DAC_WR_1); } else { - dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; - dcs_cfg |= reg_r; + dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + dcs_cfg |= reg_l; } /* Save the callibrated offset if we're in class W mode and @@ -195,7 +217,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, /* If we're applying an offset correction then updating the * callibration would be likely to introduce further offsets. */ - if (hubs->dcs_codes) + if (hubs->dcs_codes || hubs->no_series_update) return ret; /* Only need to do this if the outputs are active */ @@ -599,9 +621,6 @@ SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0, SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0, in2r_pga, ARRAY_SIZE(in2r_pga)), -/* Dummy widgets to represent differential paths */ -SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0, mixinl, ARRAY_SIZE(mixinl)), SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0, @@ -867,8 +886,11 @@ EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls); int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, int lineout1_diff, int lineout2_diff) { + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; + init_completion(&hubs->dcs_done); + snd_soc_dapm_add_routes(dapm, analogue_routes, ARRAY_SIZE(analogue_routes)); diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index f8a5e976b5e6..676b1252ab91 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -14,6 +14,9 @@ #ifndef _WM_HUBS_H #define _WM_HUBS_H +#include <linux/completion.h> +#include <linux/interrupt.h> + struct snd_soc_codec; extern const unsigned int wm_hubs_spkmix_tlv[]; @@ -23,9 +26,14 @@ struct wm_hubs_data { int dcs_codes; int dcs_readback_mode; int hp_startup_mode; + int series_startup; + int no_series_update; bool class_w; u16 class_w_dcs; + + bool dcs_done_irq; + struct completion dcs_done; }; extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); @@ -36,4 +44,6 @@ extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *, int jd_scthr, int jd_thr, int micbias1_lvl, int micbias2_lvl); +extern irqreturn_t wm_hubs_dcs_done(int irq, void *data); + #endif diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9d35b8c1a624..a49e667373bc 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -46,11 +46,28 @@ static void print_buf_info(int slot, char *name) } #endif +#define DAVINCI_PCM_FMTBITS (\ + SNDRV_PCM_FMTBIT_S8 |\ + SNDRV_PCM_FMTBIT_U8 |\ + SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE |\ + SNDRV_PCM_FMTBIT_U32_BE) + static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| + SNDRV_PCM_INFO_BATCH), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -59,7 +76,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, @@ -71,8 +88,9 @@ static struct snd_pcm_hardware pcm_hardware_playback = { static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -81,7 +99,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, @@ -139,6 +157,22 @@ struct davinci_runtime_data { struct edmacc_param ram_params; }; +static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + prtd->period++; + if (unlikely(prtd->period >= runtime->periods)) + prtd->period = 0; +} + +static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + + prtd->period = 0; +} /* * Not used with ping/pong */ @@ -199,10 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) else edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); - - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; } static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) @@ -217,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) return; if (snd_pcm_running(substream)) { + spin_lock(&prtd->lock); if (prtd->ram_channel < 0) { /* No ping/pong must fix up link dma data*/ - spin_lock(&prtd->lock); davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); } + davinci_pcm_period_elapsed(substream); + spin_unlock(&prtd->lock); snd_pcm_period_elapsed(substream); } } @@ -425,7 +456,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream, edma_read_slot(link, &prtd->asp_params); prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); - prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f); + prtd->asp_params.opt |= TCCHEN | + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* pong */ @@ -439,7 +471,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream, prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); /* interrupt after every pong completion */ prtd->asp_params.opt |= TCINTEN | TCCHEN | - EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel)); + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* ram */ @@ -527,6 +559,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: + edma_start(prtd->asp_channel); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + prtd->ram_channel >= 0) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_channel); + } + break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: edma_resume(prtd->asp_channel); @@ -550,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; + davinci_pcm_period_reset(substream); if (prtd->ram_channel >= 0) { int ret = ping_pong_dma_setup(substream); if (ret < 0) @@ -565,21 +605,31 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* copy 1st iram buffer */ - edma_start(prtd->ram_channel); - } - edma_start(prtd->asp_channel); + /* + * There is a phase offset of 2 periods between the position + * used by dma setup and the position reported in the pointer + * function. + * + * The phase offset, when not using ping-pong buffers, is due to + * the two consecutive calls to davinci_pcm_enqueue_dma() below. + * + * Whereas here, with ping-pong buffers, the phase is due to + * there being an entire buffer transfer complete before the + * first dma completion event triggers davinci_pcm_dma_irq(). + */ + davinci_pcm_period_elapsed(substream); + davinci_pcm_period_elapsed(substream); + return 0; } - prtd->period = 0; davinci_pcm_enqueue_dma(substream); + davinci_pcm_period_elapsed(substream); /* Copy self-linked parameter RAM entry into master channel */ edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); - edma_start(prtd->asp_channel); + davinci_pcm_period_elapsed(substream); return 0; } @@ -591,51 +641,23 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) struct davinci_runtime_data *prtd = runtime->private_data; unsigned int offset; int asp_count; - dma_addr_t asp_src, asp_dst; - + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + + /* + * There is a phase offset of 2 periods between the position used by dma + * setup and the position reported in the pointer function. Either +2 in + * the dma setup or -2 here in the pointer function (with wrapping, + * both) accounts for this offset -- choose the latter since it makes + * the first-time setup clearer. + */ spin_lock(&prtd->lock); - if (prtd->ram_channel >= 0) { - int ram_count; - int mod_ram; - dma_addr_t ram_src, ram_dst; - unsigned int period_size = snd_pcm_lib_period_bytes(substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* reading ram before asp should be safe - * as long as the asp transfers less than a ping size - * of bytes between the 2 reads - */ - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - edma_get_position(prtd->asp_channel, - &asp_src, &asp_dst); - asp_count = asp_src - prtd->asp_params.src; - ram_count = ram_src - prtd->ram_params.src; - mod_ram = ram_count % period_size; - mod_ram -= asp_count; - if (mod_ram < 0) - mod_ram += period_size; - else if (mod_ram == 0) { - if (snd_pcm_running(substream)) - mod_ram += period_size; - } - ram_count -= mod_ram; - if (ram_count < 0) - ram_count += period_size * runtime->periods; - } else { - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - ram_count = ram_dst - prtd->ram_params.dst; - } - asp_count = ram_count; - } else { - edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - asp_count = asp_src - runtime->dma_addr; - else - asp_count = asp_dst - runtime->dma_addr; - } + asp_count = prtd->period - 2; spin_unlock(&prtd->lock); + if (asp_count < 0) + asp_count += runtime->periods; + asp_count *= period_size; + offset = bytes_to_frames(runtime, asp_count); if (offset >= runtime->buffer_size) offset = 0; @@ -811,9 +833,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm) static u64 davinci_pcm_dmamask = 0xffffffff; -static int davinci_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret; if (!card->dev->dma_mask) diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9259f1f34899..1f11525d97e8 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -62,9 +62,9 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream) w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -80,9 +80,9 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream) /* Reset transmitter/receiver and sample rate/frame sync generators */ w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -159,6 +159,7 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: davinci_vcif_start(substream); + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c index 104e95cda0ad..c7417c76552b 100644 --- a/sound/soc/ep93xx/ep93xx-ac97.c +++ b/sound/soc/ep93xx/ep93xx-ac97.c @@ -106,12 +106,12 @@ static struct ep93xx_ac97_info *ep93xx_ac97_info; static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_out = { .name = "ac97-pcm-out", - .dma_port = EP93XX_DMA_M2P_PORT_AAC1, + .dma_port = EP93XX_DMA_AAC1, }; static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_in = { .name = "ac97-pcm-in", - .dma_port = EP93XX_DMA_M2P_PORT_AAC1, + .dma_port = EP93XX_DMA_AAC1, }; static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info, diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index 042f4e93746f..56efa0c1c9a9 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -2,7 +2,7 @@ * linux/sound/soc/ep93xx-i2s.c * EP93xx I2S driver * - * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com> + * Copyright (C) 2010 Ryan Mallon * * Based on the original driver by: * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail> @@ -70,11 +70,11 @@ struct ep93xx_i2s_info { struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = { [SNDRV_PCM_STREAM_PLAYBACK] = { .name = "i2s-pcm-out", - .dma_port = EP93XX_DMA_M2P_PORT_I2S1, + .dma_port = EP93XX_DMA_I2S1, }, [SNDRV_PCM_STREAM_CAPTURE] = { .name = "i2s-pcm-in", - .dma_port = EP93XX_DMA_M2P_PORT_I2S1, + .dma_port = EP93XX_DMA_I2S1, }, }; @@ -477,6 +477,6 @@ module_init(ep93xx_i2s_init); module_exit(ep93xx_i2s_exit); MODULE_ALIAS("platform:ep93xx-i2s"); -MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); +MODULE_AUTHOR("Ryan Mallon"); MODULE_DESCRIPTION("EP93XX I2S driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index a456e491155f..8dfd3ad84b19 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -5,7 +5,7 @@ * Copyright (C) 2006 Applied Data Systems * * Rewritten for the SoC audio subsystem (Based on PXA2xx code): - * Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.com> + * Copyright (c) 2008 Ryan Mallon * * 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 @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/slab.h> +#include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <sound/core.h> @@ -53,43 +54,34 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { struct ep93xx_runtime_data { - struct ep93xx_dma_m2p_client cl; - struct ep93xx_pcm_dma_params *params; int pointer_bytes; - struct tasklet_struct period_tasklet; int periods; - struct ep93xx_dma_buffer buf[32]; + int period_bytes; + struct dma_chan *dma_chan; + struct ep93xx_dma_data dma_data; }; -static void ep93xx_pcm_period_elapsed(unsigned long data) +static void ep93xx_pcm_dma_callback(void *data) { - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; - snd_pcm_period_elapsed(substream); -} + struct snd_pcm_substream *substream = data; + struct ep93xx_runtime_data *rtd = substream->runtime->private_data; -static void ep93xx_pcm_buffer_started(void *cookie, - struct ep93xx_dma_buffer *buf) -{ + rtd->pointer_bytes += rtd->period_bytes; + rtd->pointer_bytes %= rtd->period_bytes * rtd->periods; + + snd_pcm_period_elapsed(substream); } -static void ep93xx_pcm_buffer_finished(void *cookie, - struct ep93xx_dma_buffer *buf, - int bytes, int error) +static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) { - struct snd_pcm_substream *substream = cookie; - struct ep93xx_runtime_data *rtd = substream->runtime->private_data; - - if (buf == rtd->buf + rtd->periods - 1) - rtd->pointer_bytes = 0; - else - rtd->pointer_bytes += buf->size; + struct ep93xx_dma_data *data = filter_param; - if (!error) { - ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf); - tasklet_schedule(&rtd->period_tasklet); - } else { - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + if (data->direction == ep93xx_dma_chan_direction(chan)) { + chan->private = data; + return true; } + + return false; } static int ep93xx_pcm_open(struct snd_pcm_substream *substream) @@ -98,30 +90,38 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai; struct ep93xx_pcm_dma_params *dma_params; struct ep93xx_runtime_data *rtd; + dma_cap_mask_t mask; int ret; - dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); if (!rtd) return -ENOMEM; - memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet)); - rtd->period_tasklet.func = ep93xx_pcm_period_elapsed; - rtd->period_tasklet.data = (unsigned long)substream; - - rtd->cl.name = dma_params->name; - rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR | - ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX); - rtd->cl.cookie = substream; - rtd->cl.buffer_started = ep93xx_pcm_buffer_started; - rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished; - ret = ep93xx_dma_m2p_client_register(&rtd->cl); - if (ret < 0) { + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_CYCLIC, mask); + + dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); + rtd->dma_data.port = dma_params->dma_port; + rtd->dma_data.name = dma_params->name; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->dma_data.direction = DMA_TO_DEVICE; + else + rtd->dma_data.direction = DMA_FROM_DEVICE; + + rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter, + &rtd->dma_data); + if (!rtd->dma_chan) { kfree(rtd); - return ret; + return -EINVAL; } substream->runtime->private_data = rtd; @@ -132,31 +132,52 @@ static int ep93xx_pcm_close(struct snd_pcm_substream *substream) { struct ep93xx_runtime_data *rtd = substream->runtime->private_data; - ep93xx_dma_m2p_client_unregister(&rtd->cl); + dma_release_channel(rtd->dma_chan); kfree(rtd); return 0; } +static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct ep93xx_runtime_data *rtd = runtime->private_data; + struct dma_chan *chan = rtd->dma_chan; + struct dma_device *dma_dev = chan->device; + struct dma_async_tx_descriptor *desc; + + rtd->pointer_bytes = 0; + desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr, + rtd->period_bytes * rtd->periods, + rtd->period_bytes, + rtd->dma_data.direction); + if (!desc) + return -EINVAL; + + desc->callback = ep93xx_pcm_dma_callback; + desc->callback_param = substream; + + dmaengine_submit(desc); + return 0; +} + +static void ep93xx_pcm_dma_flush(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct ep93xx_runtime_data *rtd = runtime->private_data; + + dmaengine_terminate_all(rtd->dma_chan); +} + static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct ep93xx_runtime_data *rtd = runtime->private_data; - size_t totsize = params_buffer_bytes(params); - size_t period = params_period_bytes(params); - int i; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = totsize; - - rtd->periods = (totsize + period - 1) / period; - for (i = 0; i < rtd->periods; i++) { - rtd->buf[i].bus_addr = runtime->dma_addr + (i * period); - rtd->buf[i].size = period; - if ((i + 1) * period > totsize) - rtd->buf[i].size = totsize - (i * period); - } + rtd->periods = params_periods(params); + rtd->period_bytes = params_period_bytes(params); return 0; } @@ -168,24 +189,20 @@ static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct ep93xx_runtime_data *rtd = substream->runtime->private_data; int ret; - int i; ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - rtd->pointer_bytes = 0; - for (i = 0; i < rtd->periods; i++) - ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i); + ret = ep93xx_pcm_dma_submit(substream); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ep93xx_dma_m2p_flush(&rtd->cl); + ep93xx_pcm_dma_flush(substream); break; default: @@ -266,9 +283,11 @@ static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) static u64 ep93xx_pcm_dmamask = 0xffffffff; -static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) @@ -333,6 +352,6 @@ static void __exit ep93xx_soc_platform_exit(void) module_init(ep93xx_soc_platform_init); module_exit(ep93xx_soc_platform_exit); -MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); +MODULE_AUTHOR("Ryan Mallon"); MODULE_DESCRIPTION("EP93xx ALSA PCM interface"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/ep93xx/snappercl15.c b/sound/soc/ep93xx/snappercl15.c index dfe1d7f74ea6..c8aa8a5003ca 100644 --- a/sound/soc/ep93xx/snappercl15.c +++ b/sound/soc/ep93xx/snappercl15.c @@ -2,7 +2,7 @@ * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module * * Copyright (C) 2008 Bluewater Systems Ltd - * Author: Ryan Mallon <ryan@bluewatersys.com> + * Author: Ryan Mallon * * 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 @@ -140,7 +140,7 @@ static void __exit snappercl15_exit(void) module_init(snappercl15_init); module_exit(snappercl15_exit); -MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); +MODULE_AUTHOR("Ryan Mallon"); MODULE_DESCRIPTION("ALSA SoC Snapper CL15"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 6680c0b4d203..732208c8c0b4 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -294,9 +294,11 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) * Regardless of where the memory is actually allocated, since the device can * technically DMA to any 36-bit address, we do need to set the DMA mask to 36. */ -static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; static u64 fsl_dma_dmamask = DMA_BIT_MASK(36); int ret; @@ -939,7 +941,7 @@ static int __devinit fsl_soc_dma_probe(struct platform_device *pdev) iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); if (iprop) - dma->ssi_fifo_depth = *iprop; + dma->ssi_fifo_depth = be32_to_cpup(iprop); else /* Older 8610 DTs didn't have the fifo-depth property */ dma->ssi_fifo_depth = 8; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 313e0ccedd5b..d48afea5d93d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -678,7 +678,12 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) kfree(ssi_private); return ret; } - ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start); + ssi_private->ssi = of_iomap(np, 0); + if (!ssi_private->ssi) { + dev_err(&pdev->dev, "could not map device resources\n"); + kfree(ssi_private); + return -ENOMEM; + } ssi_private->ssi_phys = res.start; ssi_private->irq = irq_of_parse_and_map(np, 0); @@ -691,7 +696,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev) /* Determine the FIFO depth. */ iprop = of_get_property(np, "fsl,fifo-depth", NULL); if (iprop) - ssi_private->fifo_depth = *iprop; + ssi_private->fifo_depth = be32_to_cpup(iprop); else /* Older 8610 DTs didn't have the fifo-depth property */ ssi_private->fifo_depth = 8; diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index fff695ccdd3e..fd0dc46afc34 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -299,10 +299,11 @@ static struct snd_pcm_ops psc_dma_ops = { }; static u64 psc_dma_dmamask = 0xffffffff; -static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); size_t size = psc_dma_hardware.buffer_bytes_max; int rc = 0; @@ -384,7 +385,7 @@ static int mpc5200_hpcd_probe(struct of_device *op) dev_err(&op->dev, "Missing reg property\n"); return -ENODEV; } - regs = ioremap(res.start, 1 + res.end - res.start); + regs = ioremap(res.start, resource_size(&res)); if (!regs) { dev_err(&op->dev, "Could not map registers\n"); return -ENODEV; diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index c16c6b2eff95..a19297959587 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -233,7 +233,7 @@ static int get_parent_cell_index(struct device_node *np) if (!iprop) return -1; - return *iprop; + return be32_to_cpup(iprop); } /** @@ -258,7 +258,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) if (!iprop) return -EINVAL; - addr = *iprop; + addr = be32_to_cpup(iprop); bus = get_parent_cell_index(np); if (bus < 0) @@ -305,7 +305,7 @@ static int get_dma_channel(struct device_node *ssi_np, return -EINVAL; } - *dma_channel_id = *iprop; + *dma_channel_id = be32_to_cpup(iprop); *dma_id = get_parent_cell_index(dma_channel_np); of_node_put(dma_channel_np); @@ -379,7 +379,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) ret = -EINVAL; goto error; } - machine_data->ssi_id = *iprop; + machine_data->ssi_id = be32_to_cpup(iprop); /* Get the serial format and clock direction. */ sprop = of_get_property(np, "fsl,mode", NULL); @@ -405,7 +405,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) ret = -EINVAL; goto error; } - machine_data->clk_frequency = *iprop; + machine_data->clk_frequency = be32_to_cpup(iprop); } else if (strcasecmp(sprop, "i2s-master") == 0) { machine_data->dai_format = SND_SOC_DAIFMT_I2S; machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 66e0b68af147..8fa4d5f8eda1 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -232,7 +232,7 @@ static int get_parent_cell_index(struct device_node *np) iprop = of_get_property(parent, "cell-index", NULL); if (iprop) - ret = *iprop; + ret = be32_to_cpup(iprop); of_node_put(parent); @@ -261,7 +261,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) if (!iprop) return -EINVAL; - addr = *iprop; + addr = be32_to_cpup(iprop); bus = get_parent_cell_index(np); if (bus < 0) @@ -308,7 +308,7 @@ static int get_dma_channel(struct device_node *ssi_np, return -EINVAL; } - *dma_channel_id = *iprop; + *dma_channel_id = be32_to_cpup(iprop); *dma_id = get_parent_cell_index(dma_channel_np); of_node_put(dma_channel_np); @@ -379,7 +379,7 @@ static int p1022_ds_probe(struct platform_device *pdev) ret = -EINVAL; goto error; } - mdata->ssi_id = *iprop; + mdata->ssi_id = be32_to_cpup(iprop); /* Get the serial format and clock direction. */ sprop = of_get_property(np, "fsl,mode", NULL); @@ -405,7 +405,7 @@ static int p1022_ds_probe(struct platform_device *pdev) ret = -EINVAL; goto error; } - mdata->clk_frequency = *iprop; + mdata->clk_frequency = be32_to_cpup(iprop); } else if (strcasecmp(sprop, "i2s-master") == 0) { mdata->dai_format = SND_SOC_DAIFMT_I2S; mdata->codec_clk_direction = SND_SOC_CLOCK_IN; diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 4173b3d87f97..43fdc24f7e8d 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -110,12 +110,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, slave_config.direction = DMA_TO_DEVICE; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; - slave_config.dst_maxburst = dma_params->burstsize * buswidth; + slave_config.dst_maxburst = dma_params->burstsize; } else { slave_config.direction = DMA_FROM_DEVICE; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; - slave_config.src_maxburst = dma_params->burstsize * buswidth; + slave_config.src_maxburst = dma_params->burstsize; } ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c index 413b78da248f..309c59e6fb6c 100644 --- a/sound/soc/imx/imx-pcm-fiq.c +++ b/sound/soc/imx/imx-pcm-fiq.c @@ -238,12 +238,14 @@ static struct snd_pcm_ops imx_pcm_ops = { static int ssi_irq = 0; -static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret; - ret = imx_pcm_new(card, dai, pcm); + ret = imx_pcm_new(rtd); if (ret) return ret; diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 61fceb09cdb5..10a8e2783751 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -388,10 +388,11 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); -int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) { - + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index dc8a87530e3e..0a84cec3599e 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -225,8 +225,7 @@ struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev, struct imx_ssi *ssi); int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm); +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); void imx_pcm_free(struct snd_pcm *pcm); /* diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c index fb1483f7c966..a7c9578be983 100644 --- a/sound/soc/jz4740/jz4740-pcm.c +++ b/sound/soc/jz4740/jz4740-pcm.c @@ -299,9 +299,11 @@ static void jz4740_pcm_free(struct snd_pcm *pcm) static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32); -int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index e13c6ce46328..cd33de1c5b7a 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -312,9 +312,11 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm, return 0; } -static int kirkwood_dma_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret; if (!card->dev->dma_mask) diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 5a946b4115a2..3e7826058efe 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -402,9 +402,10 @@ static void sst_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int retval = 0; pr_debug("sst_pcm_new called\n"); diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index dac6732da969..9c0edad90d8b 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -356,7 +356,7 @@ static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev) nuc900_audio->irq_num = platform_get_irq(pdev, 0); if (!nuc900_audio->irq_num) { ret = -EBUSY; - goto out2; + goto out3; } nuc900_ac97_data = nuc900_audio; diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index 8263f56dc665..d589ef14e917 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -315,9 +315,12 @@ static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm) } static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32); -static int nuc900_dma_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; + if (!card->dev->dma_mask) card->dev->dma_mask = &nuc900_pcm_dmamask; if (!card->dev->coherent_dma_mask) diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 99054cf1f68f..fe83d0d176be 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -9,6 +9,9 @@ config SND_OMAP_SOC_MCBSP config SND_OMAP_SOC_MCPDM tristate +config SND_OMAP_SOC_HDMI + tristate + config SND_OMAP_SOC_N810 tristate "SoC Audio support for Nokia N810" depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C @@ -100,6 +103,14 @@ config SND_OMAP_SOC_SDP4430 Say Y if you want to add support for SoC audio on Texas Instruments SDP4430. +config SND_OMAP_SOC_OMAP4_HDMI + tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" + depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 + select SND_OMAP_SOC_HDMI + help + Say Y if you want to add support for SoC HDMI audio on Texas Instruments + OMAP4 chips + config SND_OMAP_SOC_OMAP3_PANDORA tristate "SoC Audio support for OMAP3 Pandora" depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 6c2c87eed5bb..59e2c8d1e38d 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -2,10 +2,12 @@ snd-soc-omap-objs := omap-pcm.o snd-soc-omap-mcbsp-objs := omap-mcbsp.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o +snd-soc-omap-hdmi-objs := omap-hdmi.o obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o +obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o # OMAP Machine Support snd-soc-n810-objs := n810.o @@ -21,6 +23,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-zoom2-objs := zoom2.o snd-soc-igep0020-objs := igep0020.o +snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o @@ -36,3 +39,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o +obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 462cbcbea74a..30fe0d0efe1c 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -330,7 +330,7 @@ static int cx81801_hangup(struct tty_struct *tty) return 0; } -/* Line discipline .recieve_buf() */ +/* Line discipline .receive_buf() */ static void cx81801_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { @@ -427,7 +427,8 @@ static struct snd_soc_ops ams_delta_ops = { /* Board specific codec bias level control */ static int ams_delta_set_bias_level(struct snd_soc_card *card, - enum snd_soc_bias_level level) + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) { struct snd_soc_codec *codec = card->rtd->codec; diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c new file mode 100644 index 000000000000..36c6eaeffb02 --- /dev/null +++ b/sound/soc/omap/omap-hdmi.c @@ -0,0 +1,158 @@ +/* + * omap-hdmi.c + * + * OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Jorge Candelaria <jorge.candelaria@ti.com> + * Ricardo Neri <ricardo.neri@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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <plat/dma.h> +#include "omap-pcm.h" +#include "omap-hdmi.h" + +#define DRV_NAME "hdmi-audio-dai" + +static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = { + .name = "HDMI playback", + .sync_mode = OMAP_DMA_SYNC_PACKET, +}; + +static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int err; + /* + * Make sure that the period bytes are multiple of the DMA packet size. + * Largest packet size we use is 32 32-bit words = 128 bytes + */ + err = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + if (err < 0) + return err; + + return 0; +} + +static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int err = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + omap_hdmi_dai_dma_params.packet_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + omap_hdmi_dai_dma_params.packet_size = 32; + break; + default: + err = -EINVAL; + } + + omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; + + snd_soc_dai_set_dma_data(dai, substream, + &omap_hdmi_dai_dma_params); + + return err; +} + +static struct snd_soc_dai_ops omap_hdmi_dai_ops = { + .startup = omap_hdmi_dai_startup, + .hw_params = omap_hdmi_dai_hw_params, +}; + +static struct snd_soc_dai_driver omap_hdmi_dai = { + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_HDMI_RATES, + .formats = OMAP_HDMI_FORMATS, + }, + .ops = &omap_hdmi_dai_ops, +}; + +static __devinit int omap_hdmi_probe(struct platform_device *pdev) +{ + int ret; + struct resource *hdmi_rsrc; + + hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!hdmi_rsrc) { + dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n"); + return -EINVAL; + } + + omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start + + OMAP_HDMI_AUDIO_DMA_PORT; + + hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!hdmi_rsrc) { + dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n"); + return -EINVAL; + } + + omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start; + + ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai); + return ret; +} + +static int __devexit omap_hdmi_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&pdev->dev); + return 0; +} + +static struct platform_driver hdmi_dai_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = omap_hdmi_probe, + .remove = __devexit_p(omap_hdmi_remove), +}; + +static int __init hdmi_dai_init(void) +{ + return platform_driver_register(&hdmi_dai_driver); +} +module_init(hdmi_dai_init); + +static void __exit hdmi_dai_exit(void) +{ + platform_driver_unregister(&hdmi_dai_driver); +} +module_exit(hdmi_dai_exit); + +MODULE_AUTHOR("Jorge Candelaria <jorge.candelaria@ti.com>"); +MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); +MODULE_DESCRIPTION("OMAP HDMI SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/omap-hdmi.h b/sound/soc/omap/omap-hdmi.h new file mode 100644 index 000000000000..34c298d5057e --- /dev/null +++ b/sound/soc/omap/omap-hdmi.h @@ -0,0 +1,36 @@ +/* + * omap-hdmi.h + * + * Definitions for OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Jorge Candelaria <jorge.candelaria@ti.com> + * Ricardo Neri <ricardo.neri@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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __OMAP_HDMI_H__ +#define __OMAP_HDMI_H__ + +#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c + +#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#endif diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index e6a6b991d05f..b2f5751edae3 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -366,9 +366,11 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) } } -static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c new file mode 100644 index 000000000000..9f32615b81f7 --- /dev/null +++ b/sound/soc/omap/omap4-hdmi-card.c @@ -0,0 +1,129 @@ +/* + * omap4-hdmi-card.c + * + * OMAP ALSA SoC machine driver for TI OMAP4 HDMI + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Ricardo Neri <ricardo.neri@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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <sound/pcm.h> +#include <sound/soc.h> +#include <asm/mach-types.h> +#include <video/omapdss.h> + +#define DRV_NAME "omap4-hdmi-audio" + +static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int i; + struct omap_overlay_manager *mgr = NULL; + struct device *dev = substream->pcm->card->dev; + + /* Find DSS HDMI device */ + for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { + mgr = omap_dss_get_overlay_manager(i); + if (mgr && mgr->device + && mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) + break; + } + + if (i == omap_dss_get_num_overlay_managers()) { + dev_err(dev, "HDMI display device not found!\n"); + return -ENODEV; + } + + /* Make sure HDMI is power-on to avoid L3 interconnect errors */ + if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) { + dev_err(dev, "HDMI display is not active!\n"); + return -EIO; + } + + return 0; +} + +static struct snd_soc_ops omap4_hdmi_dai_ops = { + .hw_params = omap4_hdmi_dai_hw_params, +}; + +static struct snd_soc_dai_link omap4_hdmi_dai = { + .name = "HDMI", + .stream_name = "HDMI", + .cpu_dai_name = "hdmi-audio-dai", + .platform_name = "omap-pcm-audio", + .codec_name = "omapdss_hdmi", + .codec_dai_name = "hdmi-audio-codec", + .ops = &omap4_hdmi_dai_ops, +}; + +static struct snd_soc_card snd_soc_omap4_hdmi = { + .name = "OMAP4HDMI", + .dai_link = &omap4_hdmi_dai, + .num_links = 1, +}; + +static __devinit int omap4_hdmi_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_omap4_hdmi; + int ret; + + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + card->dev = NULL; + return ret; + } + return 0; +} + +static int __devexit omap4_hdmi_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + card->dev = NULL; + return 0; +} + +static struct platform_driver omap4_hdmi_driver = { + .driver = { + .name = "omap4-hdmi-audio", + .owner = THIS_MODULE, + }, + .probe = omap4_hdmi_probe, + .remove = __devexit_p(omap4_hdmi_remove), +}; + +static int __init omap4_hdmi_init(void) +{ + return platform_driver_register(&omap4_hdmi_driver); +} +module_init(omap4_hdmi_init); + +static void __exit omap4_hdmi_exit(void) +{ + platform_driver_unregister(&omap4_hdmi_driver); +} +module_exit(omap4_hdmi_exit); + +MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); +MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 3f72d17d1ef0..9f6a758029d1 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -36,7 +36,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> #include "omap-mcbsp.h" #include "omap-pcm.h" diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 189e03900637..b80efb02bfca 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -21,6 +21,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/mfd/twl6040.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -34,8 +36,6 @@ #include "omap-pcm.h" #include "../codecs/twl6040.h" -static int twl6040_power_mode; - static int sdp4430_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -44,13 +44,13 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream, int clk_id, freq; int ret; - if (twl6040_power_mode) { - clk_id = TWL6040_SYSCLK_SEL_HPPLL; + clk_id = twl6040_get_clk_id(rtd->codec); + if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) freq = 38400000; - } else { - clk_id = TWL6040_SYSCLK_SEL_LPPLL; + else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) freq = 32768; - } + else + return -EINVAL; /* set the codec mclk */ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, @@ -81,35 +81,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = twl6040_power_mode; - return 0; -} - -static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - if (twl6040_power_mode == ucontrol->value.integer.value[0]) - return 0; - - twl6040_power_mode = ucontrol->value.integer.value[0]; - - return 1; -} - -static const char *power_texts[] = {"Low-Power", "High-Performance"}; - -static const struct soc_enum sdp4430_enum[] = { - SOC_ENUM_SINGLE_EXT(2, power_texts), -}; - -static const struct snd_kcontrol_new sdp4430_controls[] = { - SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0], - sdp4430_get_power_mode, sdp4430_set_power_mode), -}; - /* SDP4430 machine DAPM */ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Ext Mic", NULL), @@ -152,12 +123,6 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - /* Add SDP4430 specific controls */ - ret = snd_soc_add_controls(codec, sdp4430_controls, - ARRAY_SIZE(sdp4430_controls)); - if (ret) - return ret; - /* Add SDP4430 specific widgets */ ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); @@ -237,9 +202,6 @@ static int __init sdp4430_soc_init(void) if (ret) goto err; - /* Codec starts in HP mode */ - twl6040_power_mode = 1; - return 0; err: diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 01709940a43c..9a2666ffc16c 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -32,7 +32,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> #include "omap-mcbsp.h" #include "omap-pcm.h" diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index fab20a54e863..c43060053dd7 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -85,9 +85,10 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = { static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32); -static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index ab3ccaec72d2..80c85fd64e1a 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -443,10 +443,11 @@ static void s6000_pcm_free(struct snd_pcm *pcm) static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32); -static int s6000_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime) { - struct snd_soc_pcm_runtime *runtime = pcm->private_data; + struct snd_card *card = runtime->card->snd_card; + struct snd_soc_dai *dai = runtime->cpu_dai; + struct snd_pcm *pcm = runtime->pcm; struct s6000_pcm_dma_params *params; int res; diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index d155cbb58e1c..54b0e4b7faf7 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -158,7 +158,7 @@ config SND_SOC_GONI_AQUILA_WM8994 config SND_SOC_SAMSUNG_SMDK_SPDIF tristate "SoC S/PDIF Audio support for SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210) + depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310) select SND_SAMSUNG_SPDIF help Say Y if you want to add support for SoC S/PDIF audio on the SMDK. @@ -171,9 +171,23 @@ config SND_SOC_SMDK_WM8580_PCM help Say Y if you want to add support for SoC audio on the SMDK. +config SND_SOC_SMDK_WM8994_PCM + tristate "SoC PCM Audio support for WM8994 on SMDK" + depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310) + select SND_SOC_WM8994 + select SND_SAMSUNG_PCM + help + Say Y if you want to add support for SoC audio on the SMDK + config SND_SOC_SPEYSIDE tristate "Audio support for Wolfson Speyside" depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 select SND_SAMSUNG_I2S select SND_SOC_WM8915 select SND_SOC_WM9081 + +config SND_SOC_SPEYSIDE_WM8962 + tristate "Audio support for Wolfson Speyside with WM8962" + depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 + select SND_SAMSUNG_I2S + select SND_SOC_WM8962 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 683843a2744f..9eb3b12eb72f 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -35,7 +35,9 @@ snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-goni-wm8994-objs := goni_wm8994.o snd-soc-smdk-spdif-objs := smdk_spdif.o snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o +snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o snd-soc-speyside-objs := speyside.o +snd-soc-speyside-wm8962-objs := speyside_wm8962.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -54,4 +56,6 @@ obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o +obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o +obj-$(CONFIG_SND_SOC_SPEYSIDE_WM8962) += snd-soc-speyside-wm8962.o diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 5cb3b880f0d5..9465588b02f2 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -425,9 +425,11 @@ static void dma_free_dma_buffers(struct snd_pcm *pcm) static u64 dma_mask = DMA_BIT_MASK(32); -static int dma_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int dma_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h new file mode 100644 index 000000000000..c0e6d9a19efc --- /dev/null +++ b/sound/soc/samsung/i2s-regs.h @@ -0,0 +1,143 @@ +/* + * linux/sound/soc/samsung/i2s-regs.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung I2S driver's register header + * + * 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. + */ + +#ifndef __SND_SOC_SAMSUNG_I2S_REGS_H +#define __SND_SOC_SAMSUNG_I2S_REGS_H + +#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c +#define I2SAHB 0x20 +#define I2SSTR0 0x24 +#define I2SSIZE 0x28 +#define I2STRNCNT 0x2c +#define I2SLVL0ADDR 0x30 +#define I2SLVL1ADDR 0x34 +#define I2SLVL2ADDR 0x38 +#define I2SLVL3ADDR 0x3c + +#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18) + +#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12) + +#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0) + +#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ + +#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) + +#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13) + +#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0) + +#define MOD_CDCLKCON (1 << 12) + +#define PSR_PSREN (1 << 15) + +#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) + +#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7) + +#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#define AHB_INTENLVL0 (1 << 24) +#define AHB_LVL0INT (1 << 20) +#define AHB_CLRLVL0INT (1 << 16) +#define AHB_DMARLD (1 << 5) +#define AHB_INTMASK (1 << 3) +#define AHB_DMAEN (1 << 0) +#define AHB_LVLINTMASK (0xf << 20) + +#define I2SSIZE_TRNMSK (0xffff) +#define I2SSIZE_SHIFT (16) + +#endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */ + + diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 992a732b5211..c086b78539ee 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -21,110 +21,9 @@ #include <plat/audio.h> #include "dma.h" +#include "idma.h" #include "i2s.h" - -#define I2SCON 0x0 -#define I2SMOD 0x4 -#define I2SFIC 0x8 -#define I2SPSR 0xc -#define I2STXD 0x10 -#define I2SRXD 0x14 -#define I2SFICS 0x18 -#define I2STXDS 0x1c - -#define CON_RSTCLR (1 << 31) -#define CON_FRXOFSTATUS (1 << 26) -#define CON_FRXORINTEN (1 << 25) -#define CON_FTXSURSTAT (1 << 24) -#define CON_FTXSURINTEN (1 << 23) -#define CON_TXSDMA_PAUSE (1 << 20) -#define CON_TXSDMA_ACTIVE (1 << 18) - -#define CON_FTXURSTATUS (1 << 17) -#define CON_FTXURINTEN (1 << 16) -#define CON_TXFIFO2_EMPTY (1 << 15) -#define CON_TXFIFO1_EMPTY (1 << 14) -#define CON_TXFIFO2_FULL (1 << 13) -#define CON_TXFIFO1_FULL (1 << 12) - -#define CON_LRINDEX (1 << 11) -#define CON_TXFIFO_EMPTY (1 << 10) -#define CON_RXFIFO_EMPTY (1 << 9) -#define CON_TXFIFO_FULL (1 << 8) -#define CON_RXFIFO_FULL (1 << 7) -#define CON_TXDMA_PAUSE (1 << 6) -#define CON_RXDMA_PAUSE (1 << 5) -#define CON_TXCH_PAUSE (1 << 4) -#define CON_RXCH_PAUSE (1 << 3) -#define CON_TXDMA_ACTIVE (1 << 2) -#define CON_RXDMA_ACTIVE (1 << 1) -#define CON_ACTIVE (1 << 0) - -#define MOD_OPCLK_CDCLK_OUT (0 << 30) -#define MOD_OPCLK_CDCLK_IN (1 << 30) -#define MOD_OPCLK_BCLK_OUT (2 << 30) -#define MOD_OPCLK_PCLK (3 << 30) -#define MOD_OPCLK_MASK (3 << 30) -#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ - -#define MOD_BLCS_SHIFT 26 -#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) -#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) -#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) -#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) -#define MOD_BLCP_SHIFT 24 -#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) -#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) -#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) -#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) - -#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ -#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ -#define MOD_C1DD_HHALF (1 << 19) -#define MOD_C1DD_LHALF (1 << 18) -#define MOD_DC2_EN (1 << 17) -#define MOD_DC1_EN (1 << 16) -#define MOD_BLC_16BIT (0 << 13) -#define MOD_BLC_8BIT (1 << 13) -#define MOD_BLC_24BIT (2 << 13) -#define MOD_BLC_MASK (3 << 13) - -#define MOD_IMS_SYSMUX (1 << 10) -#define MOD_SLAVE (1 << 11) -#define MOD_TXONLY (0 << 8) -#define MOD_RXONLY (1 << 8) -#define MOD_TXRX (2 << 8) -#define MOD_MASK (3 << 8) -#define MOD_LR_LLOW (0 << 7) -#define MOD_LR_RLOW (1 << 7) -#define MOD_SDF_IIS (0 << 5) -#define MOD_SDF_MSB (1 << 5) -#define MOD_SDF_LSB (2 << 5) -#define MOD_SDF_MASK (3 << 5) -#define MOD_RCLK_256FS (0 << 3) -#define MOD_RCLK_512FS (1 << 3) -#define MOD_RCLK_384FS (2 << 3) -#define MOD_RCLK_768FS (3 << 3) -#define MOD_RCLK_MASK (3 << 3) -#define MOD_BCLK_32FS (0 << 1) -#define MOD_BCLK_48FS (1 << 1) -#define MOD_BCLK_16FS (2 << 1) -#define MOD_BCLK_24FS (3 << 1) -#define MOD_BCLK_MASK (3 << 1) -#define MOD_8BIT (1 << 0) - -#define MOD_CDCLKCON (1 << 12) - -#define PSR_PSREN (1 << 15) - -#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) -#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) - -#define FIC_TXFLUSH (1 << 15) -#define FIC_RXFLUSH (1 << 7) -#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) -#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) -#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) +#include "i2s-regs.h" #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) @@ -162,6 +61,7 @@ struct i2s_dai { /* DMA parameters */ struct s3c_dma_params dma_playback; struct s3c_dma_params dma_capture; + struct s3c_dma_params idma_playback; u32 quirks; u32 suspend_i2smod; u32 suspend_i2scon; @@ -979,6 +879,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, i2s->addr + I2SCON); + if (i2s->quirks & QUIRK_SEC_DAI) + idma_reg_addr_init((void *)i2s->addr, + i2s->sec_dai->idma_playback.dma_addr); + probe_exit: /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; @@ -1179,6 +1083,7 @@ static __devinit int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_size = 4; sec_dai->base = regs_base; sec_dai->quirks = quirks; + sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; } diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index e7c1009a1e1d..45fbe2b3727f 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -8,6 +8,7 @@ */ #include "../codecs/wm8994.h" +#include <sound/pcm_params.h> /* * Default CFG switch settings to use this driver: @@ -44,7 +45,9 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, int ret; /* AIF1CLK should be >=3MHz for optimal performance */ - if (params_rate(params) == 8000 || params_rate(params) == 11025) + if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE) + pll_out = params_rate(params) * 384; + else if (params_rate(params) == 8000 || params_rate(params) == 11025) pll_out = params_rate(params) * 512; else pll_out = params_rate(params) * 256; diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c new file mode 100644 index 000000000000..5f2111685480 --- /dev/null +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -0,0 +1,176 @@ +/* + * sound/soc/samsung/smdk_wm8994pcm.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * http://www.samsung.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. + */ +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "../codecs/wm8994.h" +#include "dma.h" +#include "pcm.h" + +/* + * Board Settings: + * o '1' means 'ON' + * o '0' means 'OFF' + * o 'X' means 'Don't care' + * + * SMDKC210, SMDKV310: CFG3- 1001, CFG5-1000, CFG7-111111 + */ + +/* + * Configure audio route as :- + * $ amixer sset 'DAC1' on,on + * $ amixer sset 'Right Headphone Mux' 'DAC' + * $ amixer sset 'Left Headphone Mux' 'DAC' + * $ amixer sset 'DAC1R Mixer AIF1.1' on + * $ amixer sset 'DAC1L Mixer AIF1.1' on + * $ amixer sset 'IN2L' on + * $ amixer sset 'IN2L PGA IN2LN' on + * $ amixer sset 'MIXINL IN2L' on + * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on + * $ amixer sset 'IN2R' on + * $ amixer sset 'IN2R PGA IN2RN' on + * $ amixer sset 'MIXINR IN2R' on + * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on + */ + +/* SMDK has a 16.9344MHZ crystal attached to WM8994 */ +#define SMDK_WM8994_FREQ 16934400 + +static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned long mclk_freq; + int rfs, ret; + + switch(params_rate(params)) { + case 8000: + rfs = 512; + break; + default: + dev_err(cpu_dai->dev, "%s:%d Sampling Rate %u not supported!\n", + __func__, __LINE__, params_rate(params)); + return -EINVAL; + } + + mclk_freq = params_rate(params) * rfs; + + /* Set the codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B + | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* Set the cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B + | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, + mclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + SMDK_WM8994_FREQ, mclk_freq); + if (ret < 0) + return ret; + + /* Set PCM source clock on CPU */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX, + mclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* Set SCLK_DIV for making bclk */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops smdk_wm8994_pcm_ops = { + .hw_params = smdk_wm8994_pcm_hw_params, +}; + +static struct snd_soc_dai_link smdk_dai[] = { + { + .name = "WM8994 PAIF PCM", + .stream_name = "Primary PCM", + .cpu_dai_name = "samsung-pcm.0", + .codec_dai_name = "wm8994-aif1", + .platform_name = "samsung-audio", + .codec_name = "wm8994-codec", + .ops = &smdk_wm8994_pcm_ops, + }, +}; + +static struct snd_soc_card smdk_pcm = { + .name = "SMDK-PCM", + .dai_link = smdk_dai, + .num_links = 1, +}; + +static int __devinit snd_smdk_probe(struct platform_device *pdev) +{ + int ret = 0; + + smdk_pcm.dev = &pdev->dev; + ret = snd_soc_register_card(&smdk_pcm); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + return 0; +} + +static int __devexit snd_smdk_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&smdk_pcm); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_smdk_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "samsung-smdk-pcm", + }, + .probe = snd_smdk_probe, + .remove = __devexit_p(snd_smdk_remove), +}; + +static int __init smdk_audio_init(void) +{ + return platform_driver_register(&snd_smdk_driver); +} + +module_init(smdk_audio_init); + +static void __exit smdk_audio_exit(void) +{ + platform_driver_unregister(&snd_smdk_driver); +} + +module_exit(smdk_audio_exit); + +MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>"); +MODULE_DESCRIPTION("ALSA SoC SMDK WM8994 for PCM"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 360a333cb7c0..d6dee4d02036 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -20,24 +20,29 @@ #define WM8915_HPSEL_GPIO 214 static int speyside_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; int ret; + if (dapm->dev != codec_dai->dev) + return 0; + switch (level) { case SND_SOC_BIAS_STANDBY: - ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1, + ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1, + ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2, 0, 0, 0); if (ret < 0) { pr_err("Failed to stop FLL\n"); return ret; } + break; default: break; @@ -46,6 +51,45 @@ static int speyside_set_bias_level(struct snd_soc_card *card, return 0; } +static int speyside_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + int ret; + + if (dapm->dev != codec_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + WM8915_FLL_MCLK2, + 32768, 48000 * 256); + if (ret < 0) { + pr_err("Failed to start FLL\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8915_SYSCLK_FLL, + 48000 * 256, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + } + break; + + default: + break; + } + + card->dapm.bias_level = level; + + return 0; +} + static int speyside_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -66,16 +110,6 @@ static int speyside_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1, - 32768, 256 * 48000); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL, - 256 * 48000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - return 0; } @@ -127,7 +161,7 @@ static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; int ret; - ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0); + ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0); if (ret < 0) return ret; @@ -267,6 +301,7 @@ static struct snd_soc_card speyside = { .num_configs = ARRAY_SIZE(speyside_codec_conf), .set_bias_level = speyside_set_bias_level, + .set_bias_level_post = speyside_set_bias_level_post, .controls = controls, .num_controls = ARRAY_SIZE(controls), diff --git a/sound/soc/samsung/speyside_wm8962.c b/sound/soc/samsung/speyside_wm8962.c new file mode 100644 index 000000000000..8ac42bf82090 --- /dev/null +++ b/sound/soc/samsung/speyside_wm8962.c @@ -0,0 +1,264 @@ +/* + * Speyside with WM8962 audio support + * + * Copyright 2011 Wolfson Microelectronics + * + * 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. + */ + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <linux/gpio.h> + +#include "../codecs/wm8962.h" + +static int speyside_wm8962_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + int ret; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, + WM8962_FLL_MCLK, 32768, + 44100 * 256); + if (ret < 0) + pr_err("Failed to start FLL: %d\n", ret); + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8962_SYSCLK_FLL, + 44100 * 256, + SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to set SYSCLK: %d\n"); + return ret; + } + } + break; + + default: + break; + } + + return 0; +} + +static int speyside_wm8962_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + int ret; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to switch away from FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, + 0, 0, 0); + if (ret < 0) { + pr_err("Failed to stop FLL: %d\n", ret); + return ret; + } + break; + + default: + break; + } + + dapm->bias_level = level; + + return 0; +} + +static int speyside_wm8962_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops speyside_wm8962_ops = { + .hw_params = speyside_wm8962_hw_params, +}; + +static struct snd_soc_dai_link speyside_wm8962_dai[] = { + { + .name = "CPU", + .stream_name = "CPU", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8962", + .platform_name = "samsung-audio", + .codec_name = "wm8962.1-001a", + .ops = &speyside_wm8962_ops, + }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Main Speaker"), +}; + +static struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + + SND_SOC_DAPM_MIC("DMIC", NULL), + + SND_SOC_DAPM_SPK("Main Speaker", NULL), +}; + +static struct snd_soc_dapm_route audio_paths[] = { + { "Headphone", NULL, "HPOUTL" }, + { "Headphone", NULL, "HPOUTR" }, + + { "Main Speaker", NULL, "SPKOUTL" }, + { "Main Speaker", NULL, "SPKOUTR" }, + + { "MICBIAS", NULL, "Headset Mic" }, + { "IN4L", NULL, "MICBIAS" }, + { "IN4R", NULL, "MICBIAS" }, + + { "MICBIAS", NULL, "DMIC" }, + { "DMICDAT", NULL, "MICBIAS" }, +}; + +static struct snd_soc_jack speyside_wm8962_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin speyside_wm8962_headset_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int speyside_wm8962_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_codec *codec = card->rtd[0].codec; + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_jack_new(codec, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &speyside_wm8962_headset); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&speyside_wm8962_headset, + ARRAY_SIZE(speyside_wm8962_headset_pins), + speyside_wm8962_headset_pins); + if (ret) + return ret; + + wm8962_mic_detect(codec, &speyside_wm8962_headset); + + return 0; +} + +static struct snd_soc_card speyside_wm8962 = { + .name = "Speyside WM8962", + .dai_link = speyside_wm8962_dai, + .num_links = ARRAY_SIZE(speyside_wm8962_dai), + + .set_bias_level = speyside_wm8962_set_bias_level, + .set_bias_level_post = speyside_wm8962_set_bias_level_post, + + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + + .late_probe = speyside_wm8962_late_probe, +}; + +static __devinit int speyside_wm8962_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &speyside_wm8962; + int ret; + + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + return ret; + } + + return 0; +} + +static int __devexit speyside_wm8962_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver speyside_wm8962_driver = { + .driver = { + .name = "speyside-wm8962", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = speyside_wm8962_probe, + .remove = __devexit_p(speyside_wm8962_remove), +}; + +static int __init speyside_wm8962_audio_init(void) +{ + return platform_driver_register(&speyside_wm8962_driver); +} +module_init(speyside_wm8962_audio_init); + +static void __exit speyside_wm8962_audio_exit(void) +{ + platform_driver_unregister(&speyside_wm8962_driver); +} +module_exit(speyside_wm8962_audio_exit); + +MODULE_DESCRIPTION("Speyside WM8962 audio support"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:speyside-wm8962"); diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index c326d29992fe..db74005f37ce 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -327,10 +327,10 @@ static void camelot_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static int camelot_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_pcm *pcm = rtd->pcm; + /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel * in MMAP mode (i.e. aplay -M) */ diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 4a9da6b5f4e1..8e112ccffb13 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -118,10 +118,38 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena /* * FSI driver use below type name for variable * - * xxx_len : data length - * xxx_width : data width - * xxx_offset : data offset * xxx_num : number of data + * xxx_pos : position of data + * xxx_capa : capacity of data + */ + +/* + * period/frame/sample image + * + * ex) PCM (2ch) + * + * period pos period pos + * [n] [n + 1] + * |<-------------------- period--------------------->| + * ==|============================================ ... =|== + * | | + * ||<----- frame ----->|<------ frame ----->| ... | + * |+--------------------+--------------------+- ... | + * ||[ sample ][ sample ]|[ sample ][ sample ]| ... | + * |+--------------------+--------------------+- ... | + * ==|============================================ ... =|== + */ + +/* + * FSI FIFO image + * + * | | + * | | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * --> go to codecs */ /* @@ -131,12 +159,11 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena struct fsi_stream { struct snd_pcm_substream *substream; - int fifo_max_num; - - int buff_offset; - int buff_len; - int period_len; - int period_num; + int fifo_sample_capa; /* sample capacity of FSI FIFO */ + int buff_sample_capa; /* sample capacity of ALSA buffer */ + int buff_sample_pos; /* sample position of ALSA buffer */ + int period_samples; /* sample number / 1 period */ + int period_pos; /* current period position */ int uerr_num; int oerr_num; @@ -149,17 +176,14 @@ struct fsi_priv { struct fsi_stream playback; struct fsi_stream capture; + u32 do_fmt; + u32 di_fmt; + int chan_num:16; int clk_master:1; + int spdif:1; long rate; - - /* for suspend/resume */ - u32 saved_do_fmt; - u32 saved_di_fmt; - u32 saved_ckg1; - u32 saved_ckg2; - u32 saved_out_sel; }; struct fsi_core { @@ -180,14 +204,6 @@ struct fsi_master { struct fsi_core *core; struct sh_fsi_platform_info *info; spinlock_t lock; - - /* for suspend/resume */ - u32 saved_a_mclk; - u32 saved_b_mclk; - u32 saved_iemsk; - u32 saved_imsk; - u32 saved_clk_rst; - u32 saved_soft_rst; }; /* @@ -271,6 +287,11 @@ static int fsi_is_port_a(struct fsi_priv *fsi) return fsi->master->base == fsi->base; } +static int fsi_is_spdif(struct fsi_priv *fsi) +{ + return fsi->spdif; +} + static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -342,28 +363,59 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) return shift; } +static int fsi_frame2sample(struct fsi_priv *fsi, int frames) +{ + return frames * fsi->chan_num; +} + +static int fsi_sample2frame(struct fsi_priv *fsi, int samples) +{ + return samples / fsi->chan_num; +} + +static int fsi_stream_is_working(struct fsi_priv *fsi, + int is_play) +{ + struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + int ret; + + spin_lock_irqsave(&master->lock, flags); + ret = !!io->substream; + spin_unlock_irqrestore(&master->lock, flags); + + return ret; +} + static void fsi_stream_push(struct fsi_priv *fsi, int is_play, - struct snd_pcm_substream *substream, - u32 buffer_len, - u32 period_len) + struct snd_pcm_substream *substream) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + spin_lock_irqsave(&master->lock, flags); io->substream = substream; - io->buff_len = buffer_len; - io->buff_offset = 0; - io->period_len = period_len; - io->period_num = 0; + io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size); + io->buff_sample_pos = 0; + io->period_samples = fsi_frame2sample(fsi, runtime->period_size); + io->period_pos = 0; io->oerr_num = -1; /* ignore 1st err */ io->uerr_num = -1; /* ignore 1st err */ + spin_unlock_irqrestore(&master->lock, flags); } static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); struct snd_soc_dai *dai = fsi_get_dai(io->substream); + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + spin_lock_irqsave(&master->lock, flags); if (io->oerr_num > 0) dev_err(dai->dev, "over_run = %d\n", io->oerr_num); @@ -372,47 +424,27 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) dev_err(dai->dev, "under_run = %d\n", io->uerr_num); io->substream = NULL; - io->buff_len = 0; - io->buff_offset = 0; - io->period_len = 0; - io->period_num = 0; + io->buff_sample_capa = 0; + io->buff_sample_pos = 0; + io->period_samples = 0; + io->period_pos = 0; io->oerr_num = 0; io->uerr_num = 0; + spin_unlock_irqrestore(&master->lock, flags); } -static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) +static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play) { u32 status; - int data_num; + int frames; status = is_play ? fsi_reg_read(fsi, DOFF_ST) : fsi_reg_read(fsi, DIFF_ST); - data_num = 0x1ff & (status >> 8); - data_num *= fsi->chan_num; - - return data_num; -} - -static int fsi_len2num(int len, int width) -{ - return len / width; -} - -#define fsi_num2offset(a, b) fsi_num2len(a, b) -static int fsi_num2len(int num, int width) -{ - return num * width; -} - -static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) -{ - struct fsi_stream *io = fsi_get_stream(fsi, is_play); - struct snd_pcm_substream *substream = io->substream; - struct snd_pcm_runtime *runtime = substream->runtime; + frames = 0x1ff & (status >> 8); - return frames_to_bytes(runtime, 1) / fsi->chan_num; + return fsi_frame2sample(fsi, frames); } static void fsi_count_fifo_err(struct fsi_priv *fsi) @@ -444,8 +476,10 @@ static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream) { int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_pcm_runtime *runtime = io->substream->runtime; - return io->substream->runtime->dma_area + io->buff_offset; + return runtime->dma_area + + samples_to_bytes(runtime, io->buff_sample_pos); } static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num) @@ -559,37 +593,94 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) /* * clock function */ -#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1) -#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0) -static void __fsi_module_clk_ctrl(struct fsi_master *master, - struct device *dev, - int enable) +static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, + long rate, int enable) { - pm_runtime_get_sync(dev); + struct fsi_master *master = fsi_get_master(fsi); + set_rate_func set_rate = fsi_get_info_set_rate(master); + int fsi_ver = master->core->ver; + int ret; - if (enable) { - /* enable only SR */ - fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR); - fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0); - } else { - /* clear all registers */ - fsi_master_mask_set(master, SOFT_RST, FSISR, 0); + ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable); + if (ret < 0) /* error */ + return ret; + + if (!enable) + return 0; + + if (ret > 0) { + u32 data = 0; + + switch (ret & SH_FSI_ACKMD_MASK) { + default: + /* FALL THROUGH */ + case SH_FSI_ACKMD_512: + data |= (0x0 << 12); + break; + case SH_FSI_ACKMD_256: + data |= (0x1 << 12); + break; + case SH_FSI_ACKMD_128: + data |= (0x2 << 12); + break; + case SH_FSI_ACKMD_64: + data |= (0x3 << 12); + break; + case SH_FSI_ACKMD_32: + if (fsi_ver < 2) + dev_err(dev, "unsupported ACKMD\n"); + else + data |= (0x4 << 12); + break; + } + + switch (ret & SH_FSI_BPFMD_MASK) { + default: + /* FALL THROUGH */ + case SH_FSI_BPFMD_32: + data |= (0x0 << 8); + break; + case SH_FSI_BPFMD_64: + data |= (0x1 << 8); + break; + case SH_FSI_BPFMD_128: + data |= (0x2 << 8); + break; + case SH_FSI_BPFMD_256: + data |= (0x3 << 8); + break; + case SH_FSI_BPFMD_512: + data |= (0x4 << 8); + break; + case SH_FSI_BPFMD_16: + if (fsi_ver < 2) + dev_err(dev, "unsupported ACKMD\n"); + else + data |= (0x7 << 8); + break; + } + + fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); + udelay(10); + ret = 0; } - pm_runtime_put_sync(dev); + return ret; } -#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1) -#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0) -static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable) +#define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1) +#define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0) +static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) { struct fsi_master *master = fsi_get_master(fsi); - u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR; u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; - int is_master = fsi_is_clk_master(fsi); - fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0); - if (is_master) + if (enable) + fsi_irq_enable(fsi, is_play); + else + fsi_irq_disable(fsi, is_play); + + if (fsi_is_clk_master(fsi)) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); } @@ -598,18 +689,19 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable) */ static void fsi_fifo_init(struct fsi_priv *fsi, int is_play, - struct snd_soc_dai *dai) + struct device *dev) { struct fsi_master *master = fsi_get_master(fsi); struct fsi_stream *io = fsi_get_stream(fsi, is_play); u32 shift, i; + int frame_capa; /* get on-chip RAM capacity */ shift = fsi_master_read(master, FIFO_SZ); shift >>= fsi_get_port_shift(fsi, is_play); shift &= FIFO_SZ_MASK; - io->fifo_max_num = 256 << shift; - dev_dbg(dai->dev, "fifo = %d words\n", io->fifo_max_num); + frame_capa = 256 << shift; + dev_dbg(dev, "fifo = %d words\n", frame_capa); /* * The maximum number of sample data varies depending @@ -631,9 +723,11 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 8 channels: 32 ( 32 x 8 = 256) */ for (i = 1; i < fsi->chan_num; i <<= 1) - io->fifo_max_num >>= 1; - dev_dbg(dai->dev, "%d channel %d store\n", - fsi->chan_num, io->fifo_max_num); + frame_capa >>= 1; + dev_dbg(dev, "%d channel %d store\n", + fsi->chan_num, frame_capa); + + io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa); /* * set interrupt generation factor @@ -654,10 +748,10 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) struct snd_pcm_substream *substream = NULL; int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - int data_residue_num; - int data_num; - int data_num_max; - int ch_width; + int sample_residues; + int sample_width; + int samples; + int samples_max; int over_period; void (*fn)(struct fsi_priv *fsi, int size); @@ -673,36 +767,35 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) /* FSI FIFO has limit. * So, this driver can not send periods data at a time */ - if (io->buff_offset >= - fsi_num2offset(io->period_num + 1, io->period_len)) { + if (io->buff_sample_pos >= + io->period_samples * (io->period_pos + 1)) { over_period = 1; - io->period_num = (io->period_num + 1) % runtime->periods; + io->period_pos = (io->period_pos + 1) % runtime->periods; - if (0 == io->period_num) - io->buff_offset = 0; + if (0 == io->period_pos) + io->buff_sample_pos = 0; } - /* get 1 channel data width */ - ch_width = fsi_get_frame_width(fsi, is_play); + /* get 1 sample data width */ + sample_width = samples_to_bytes(runtime, 1); - /* get residue data number of alsa */ - data_residue_num = fsi_len2num(io->buff_len - io->buff_offset, - ch_width); + /* get number of residue samples */ + sample_residues = io->buff_sample_capa - io->buff_sample_pos; if (is_play) { /* * for play-back * - * data_num_max : number of FSI fifo free space - * data_num : number of ALSA residue data + * samples_max : number of FSI fifo free samples space + * samples : number of ALSA residue samples */ - data_num_max = io->fifo_max_num * fsi->chan_num; - data_num_max -= fsi_get_fifo_data_num(fsi, is_play); + samples_max = io->fifo_sample_capa; + samples_max -= fsi_get_current_fifo_samples(fsi, is_play); - data_num = data_residue_num; + samples = sample_residues; - switch (ch_width) { + switch (sample_width) { case 2: fn = fsi_dma_soft_push16; break; @@ -716,13 +809,13 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) /* * for capture * - * data_num_max : number of ALSA free space - * data_num : number of data in FSI fifo + * samples_max : number of ALSA free samples space + * samples : number of samples in FSI fifo */ - data_num_max = data_residue_num; - data_num = fsi_get_fifo_data_num(fsi, is_play); + samples_max = sample_residues; + samples = fsi_get_current_fifo_samples(fsi, is_play); - switch (ch_width) { + switch (sample_width) { case 2: fn = fsi_dma_soft_pop16; break; @@ -734,12 +827,12 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) } } - data_num = min(data_num, data_num_max); + samples = min(samples, samples_max); - fn(fsi, data_num); + fn(fsi, samples); - /* update buff_offset */ - io->buff_offset += fsi_num2offset(data_num, ch_width); + /* update buff_sample_pos */ + io->buff_sample_pos += samples; if (over_period) snd_pcm_period_elapsed(substream); @@ -788,16 +881,20 @@ static irqreturn_t fsi_interrupt(int irq, void *data) * dai ops */ -static int fsi_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int fsi_hw_startup(struct fsi_priv *fsi, + int is_play, + struct device *dev) { - struct fsi_priv *fsi = fsi_get_priv(substream); u32 flags = fsi_get_info_flags(fsi); - u32 data; - int is_play = fsi_is_play(substream); + u32 data = 0; - pm_runtime_get_sync(dai->dev); + pm_runtime_get_sync(dev); + /* clock setting */ + if (fsi_is_clk_master(fsi)) + data = DIMD | DOMD; + + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); /* clock inversion (CKG2) */ data = 0; @@ -812,54 +909,70 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); + /* set format */ + fsi_reg_write(fsi, DO_FMT, fsi->do_fmt); + fsi_reg_write(fsi, DI_FMT, fsi->di_fmt); + + /* spdif ? */ + if (fsi_is_spdif(fsi)) { + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + } + /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); /* fifo init */ - fsi_fifo_init(fsi, is_play, dai); + fsi_fifo_init(fsi, is_play, dev); return 0; } -static void fsi_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static void fsi_hw_shutdown(struct fsi_priv *fsi, + int is_play, + struct device *dev) +{ + if (fsi_is_clk_master(fsi)) + fsi_set_master_clk(dev, fsi, fsi->rate, 0); + + pm_runtime_put_sync(dev); +} + +static int fsi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); - struct fsi_master *master = fsi_get_master(fsi); - set_rate_func set_rate = fsi_get_info_set_rate(master); - fsi_irq_disable(fsi, is_play); + return fsi_hw_startup(fsi, is_play, dai->dev); +} - if (fsi_is_clk_master(fsi)) - set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); +static void fsi_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + int is_play = fsi_is_play(substream); + fsi_hw_shutdown(fsi, is_play, dai->dev); fsi->rate = 0; - - pm_runtime_put_sync(dai->dev); } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct snd_pcm_runtime *runtime = substream->runtime; int is_play = fsi_is_play(substream); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - fsi_stream_push(fsi, is_play, substream, - frames_to_bytes(runtime, runtime->buffer_size), - frames_to_bytes(runtime, runtime->period_size)); + fsi_stream_push(fsi, is_play, substream); ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); - fsi_irq_enable(fsi, is_play); - fsi_port_start(fsi); + fsi_port_start(fsi, is_play); break; case SNDRV_PCM_TRIGGER_STOP: - fsi_port_stop(fsi); - fsi_irq_disable(fsi, is_play); + fsi_port_stop(fsi, is_play); fsi_stream_pop(fsi, is_play); break; } @@ -884,8 +997,8 @@ static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) return -EINVAL; } - fsi_reg_write(fsi, DO_FMT, data); - fsi_reg_write(fsi, DI_FMT, data); + fsi->do_fmt = data; + fsi->di_fmt = data; return 0; } @@ -900,11 +1013,10 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi) data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; fsi->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + fsi->spdif = 1; - fsi_reg_write(fsi, DO_FMT, data); - fsi_reg_write(fsi, DI_FMT, data); + fsi->do_fmt = data; + fsi->di_fmt = data; return 0; } @@ -915,32 +1027,24 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct fsi_master *master = fsi_get_master(fsi); set_rate_func set_rate = fsi_get_info_set_rate(master); u32 flags = fsi_get_info_flags(fsi); - u32 data = 0; int ret; - pm_runtime_get_sync(dai->dev); - /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - data = DIMD | DOMD; fsi->clk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: - ret = -EINVAL; - goto set_fmt_exit; + return -EINVAL; } if (fsi_is_clk_master(fsi) && !set_rate) { dev_err(dai->dev, "platform doesn't have set_rate\n"); - ret = -EINVAL; - goto set_fmt_exit; + return -EINVAL; } - fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); - /* set format */ switch (flags & SH_FSI_FMT_MASK) { case SH_FSI_FMT_DAI: @@ -953,9 +1057,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ret = -EINVAL; } -set_fmt_exit: - pm_runtime_put_sync(dai->dev); - return ret; } @@ -964,79 +1065,19 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - set_rate_func set_rate = fsi_get_info_set_rate(master); - int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; if (!fsi_is_clk_master(fsi)) return 0; - ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1); - if (ret < 0) /* error */ + ret = fsi_set_master_clk(dai->dev, fsi, rate, 1); + if (ret < 0) return ret; fsi->rate = rate; - if (ret > 0) { - u32 data = 0; - - switch (ret & SH_FSI_ACKMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_ACKMD_512: - data |= (0x0 << 12); - break; - case SH_FSI_ACKMD_256: - data |= (0x1 << 12); - break; - case SH_FSI_ACKMD_128: - data |= (0x2 << 12); - break; - case SH_FSI_ACKMD_64: - data |= (0x3 << 12); - break; - case SH_FSI_ACKMD_32: - if (fsi_ver < 2) - dev_err(dai->dev, "unsupported ACKMD\n"); - else - data |= (0x4 << 12); - break; - } - - switch (ret & SH_FSI_BPFMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_BPFMD_32: - data |= (0x0 << 8); - break; - case SH_FSI_BPFMD_64: - data |= (0x1 << 8); - break; - case SH_FSI_BPFMD_128: - data |= (0x2 << 8); - break; - case SH_FSI_BPFMD_256: - data |= (0x3 << 8); - break; - case SH_FSI_BPFMD_512: - data |= (0x4 << 8); - break; - case SH_FSI_BPFMD_16: - if (fsi_ver < 2) - dev_err(dai->dev, "unsupported ACKMD\n"); - else - data |= (0x7 << 8); - break; - } - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - ret = 0; - } return ret; - } static struct snd_soc_dai_ops fsi_dai_ops = { @@ -1097,16 +1138,14 @@ static int fsi_hw_free(struct snd_pcm_substream *substream) static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream)); - long location; + int samples_pos = io->buff_sample_pos - 1; - location = (io->buff_offset - 1); - if (location < 0) - location = 0; + if (samples_pos < 0) + samples_pos = 0; - return bytes_to_frames(runtime, location); + return fsi_sample2frame(fsi, samples_pos); } static struct snd_pcm_ops fsi_pcm_ops = { @@ -1129,10 +1168,10 @@ static void fsi_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static int fsi_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_pcm *pcm = rtd->pcm; + /* * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel * in MMAP mode (i.e. aplay -M) @@ -1246,8 +1285,6 @@ static int fsi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); dev_set_drvdata(&pdev->dev, master); - fsi_module_init(master, &pdev->dev); - ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, id_entry->name, master); if (ret) { @@ -1290,8 +1327,6 @@ static int fsi_remove(struct platform_device *pdev) master = dev_get_drvdata(&pdev->dev); - fsi_module_kill(master, &pdev->dev); - free_irq(master->irq, master); pm_runtime_disable(&pdev->dev); @@ -1305,53 +1340,43 @@ static int fsi_remove(struct platform_device *pdev) } static void __fsi_suspend(struct fsi_priv *fsi, - struct device *dev, - set_rate_func set_rate) + int is_play, + struct device *dev) { - fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT); - fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT); - fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1); - fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2); - fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL); + if (!fsi_stream_is_working(fsi, is_play)) + return; - if (fsi_is_clk_master(fsi)) - set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0); + fsi_port_stop(fsi, is_play); + fsi_hw_shutdown(fsi, is_play, dev); } static void __fsi_resume(struct fsi_priv *fsi, - struct device *dev, - set_rate_func set_rate) + int is_play, + struct device *dev) { - fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt); - fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt); - fsi_reg_write(fsi, CKG1, fsi->saved_ckg1); - fsi_reg_write(fsi, CKG2, fsi->saved_ckg2); - fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel); + if (!fsi_stream_is_working(fsi, is_play)) + return; + + fsi_hw_startup(fsi, is_play, dev); + + if (fsi_is_clk_master(fsi) && fsi->rate) + fsi_set_master_clk(dev, fsi, fsi->rate, 1); + + fsi_port_start(fsi, is_play); - if (fsi_is_clk_master(fsi)) - set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1); } static int fsi_suspend(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); - set_rate_func set_rate = fsi_get_info_set_rate(master); - - pm_runtime_get_sync(dev); - - __fsi_suspend(&master->fsia, dev, set_rate); - __fsi_suspend(&master->fsib, dev, set_rate); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; - master->saved_a_mclk = fsi_core_read(master, a_mclk); - master->saved_b_mclk = fsi_core_read(master, b_mclk); - master->saved_iemsk = fsi_core_read(master, iemsk); - master->saved_imsk = fsi_core_read(master, imsk); - master->saved_clk_rst = fsi_master_read(master, CLK_RST); - master->saved_soft_rst = fsi_master_read(master, SOFT_RST); + __fsi_suspend(fsia, 1, dev); + __fsi_suspend(fsia, 0, dev); - fsi_module_kill(master, dev); - - pm_runtime_put_sync(dev); + __fsi_suspend(fsib, 1, dev); + __fsi_suspend(fsib, 0, dev); return 0; } @@ -1359,23 +1384,14 @@ static int fsi_suspend(struct device *dev) static int fsi_resume(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); - set_rate_func set_rate = fsi_get_info_set_rate(master); - - pm_runtime_get_sync(dev); - - fsi_module_init(master, dev); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; - fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst); - fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst); - fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk); - fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk); - fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk); - fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk); + __fsi_resume(fsia, 1, dev); + __fsi_resume(fsia, 0, dev); - __fsi_resume(&master->fsia, dev, set_rate); - __fsi_resume(&master->fsib, dev, set_rate); - - pm_runtime_put_sync(dev); + __fsi_resume(fsib, 1, dev); + __fsi_resume(fsib, 0, dev); return 0; } diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index a423babcf145..f8f681690a71 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -527,10 +527,11 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss) return bytes_to_frames(ss->runtime, ptr); } -static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd) { /* card->dev == socdev->dev, see snd_soc_new_pcms() */ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; struct siu_info *info = siu_i2s_data; struct platform_device *pdev = to_platform_device(card->dev); int ret; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 039b9532b270..d9f8aded51f3 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -20,422 +20,6 @@ #include <trace/events/asoc.h> -#ifdef CONFIG_SPI_MASTER -static int do_spi_write(void *control, const char *data, int len) -{ - struct spi_device *spi = control; - int ret; - - ret = spi_write(spi, data, len); - if (ret < 0) - return ret; - - return len; -} -#endif - -static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value, const void *data, int len) -{ - int ret; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, len); - if (ret == len) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg) -{ - int ret; - unsigned int val; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; -} - -static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data; - - data = cpu_to_be16((reg << 12) | (value & 0xffffff)); - - return do_hw_write(codec, reg, value, &data, 2); -} - -static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; - - return do_hw_write(codec, reg, value, data, 2); -} - -static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - reg &= 0xff; - data[0] = reg; - data[1] = value & 0xff; - - return do_hw_write(codec, reg, value, data, 2); -} - -static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[3]; - - data[0] = reg; - data[1] = (value >> 8) & 0xff; - data[2] = value & 0xff; - - return do_hw_write(codec, reg, value, data, 3); -} - -static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int do_i2c_read(struct snd_soc_codec *codec, - void *reg, int reglen, - void *data, int datalen) -{ - struct i2c_msg xfer[2]; - int ret; - struct i2c_client *client = codec->control_data; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = reglen; - xfer[0].buf = reg; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = datalen; - xfer[1].buf = data; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret == 2) - return 0; - else if (ret < 0) - return ret; - else - return -EIO; -} -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u8 reg = r; - u8 data; - int ret; - - ret = do_i2c_read(codec, ®, 1, &data, 1); - if (ret < 0) - return 0; - return data; -} -#else -#define snd_soc_8_8_read_i2c NULL -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u8 reg = r; - u16 data; - int ret; - - ret = do_i2c_read(codec, ®, 1, &data, 2); - if (ret < 0) - return 0; - return (data >> 8) | ((data & 0xff) << 8); -} -#else -#define snd_soc_8_16_read_i2c NULL -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u16 reg = r; - u8 data; - int ret; - - ret = do_i2c_read(codec, ®, 2, &data, 1); - if (ret < 0) - return 0; - return data; -} -#else -#define snd_soc_16_8_read_i2c NULL -#endif - -static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[3]; - - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; - data[2] = value; - - return do_hw_write(codec, reg, value, data, 3); -} - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u16 reg = cpu_to_be16(r); - u16 data; - int ret; - - ret = do_i2c_read(codec, ®, 2, &data, 2); - if (ret < 0) - return 0; - return be16_to_cpu(data); -} -#else -#define snd_soc_16_16_read_i2c NULL -#endif - -static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[4]; - - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; - data[2] = (value >> 8) & 0xff; - data[3] = value & 0xff; - - return do_hw_write(codec, reg, value, data, 4); -} - -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects - * including any leading register specific data. Any data written - * through this function will not go through the cache as it only - * handles writing to volatile or out of bounds registers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, - const void *data, size_t len) -{ - int ret; - - /* To ensure that we don't get out of sync with the cache, check - * whether the base register is volatile or if we've directly asked - * to bypass the cache. Out of bounds registers are considered - * volatile. - */ - if (!codec->cache_bypass - && !snd_soc_codec_volatile_register(codec, reg) - && reg < codec->driver->reg_cache_size) - return -EINVAL; - - switch (codec->control_type) { -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - case SND_SOC_I2C: - ret = i2c_master_send(codec->control_data, data, len); - break; -#endif -#if defined(CONFIG_SPI_MASTER) - case SND_SOC_SPI: - ret = spi_write(codec->control_data, data, len); - break; -#endif - default: - BUG(); - } - - if (ret == len) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -static struct { - int addr_bits; - int data_bits; - int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); - unsigned int (*read)(struct snd_soc_codec *, unsigned int); - unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); -} io_types[] = { - { - .addr_bits = 4, .data_bits = 12, - .write = snd_soc_4_12_write, .read = snd_soc_4_12_read, - }, - { - .addr_bits = 7, .data_bits = 9, - .write = snd_soc_7_9_write, .read = snd_soc_7_9_read, - }, - { - .addr_bits = 8, .data_bits = 8, - .write = snd_soc_8_8_write, .read = snd_soc_8_8_read, - .i2c_read = snd_soc_8_8_read_i2c, - }, - { - .addr_bits = 8, .data_bits = 16, - .write = snd_soc_8_16_write, .read = snd_soc_8_16_read, - .i2c_read = snd_soc_8_16_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 8, - .write = snd_soc_16_8_write, .read = snd_soc_16_8_read, - .i2c_read = snd_soc_16_8_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 16, - .write = snd_soc_16_16_write, .read = snd_soc_16_16_read, - .i2c_read = snd_soc_16_16_read_i2c, - }, -}; - -/** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. - * - * @codec: CODEC to configure. - * @addr_bits: Number of bits of register address data. - * @data_bits: Number of bits of data per register. - * @control: Control bus used. - * - * Register formats are frequently shared between many I2C and SPI - * devices. In order to promote code reuse the ASoC core provides - * some standard implementations of CODEC read and write operations - * which can be set up using this function. - * - * The caller is responsible for allocating and initialising the - * actual cache. - * - * Note that at present this code cannot be used by CODECs with - * volatile registers. - */ -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - int addr_bits, int data_bits, - enum snd_soc_control_type control) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(io_types); i++) - if (io_types[i].addr_bits == addr_bits && - io_types[i].data_bits == data_bits) - break; - if (i == ARRAY_SIZE(io_types)) { - printk(KERN_ERR - "No I/O functions for %d bit address %d bit data\n", - addr_bits, data_bits); - return -EINVAL; - } - - codec->write = io_types[i].write; - codec->read = io_types[i].read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; - - switch (control) { - case SND_SOC_I2C: -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - codec->hw_write = (hw_write_t)i2c_master_send; -#endif - if (io_types[i].i2c_read) - codec->hw_read = io_types[i].i2c_read; - - codec->control_data = container_of(codec->dev, - struct i2c_client, - dev); - break; - - case SND_SOC_SPI: -#ifdef CONFIG_SPI_MASTER - codec->hw_write = do_spi_write; -#endif - - codec->control_data = container_of(codec->dev, - struct spi_device, - dev); - break; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); - static bool snd_soc_set_cache_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size) { @@ -483,31 +67,86 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, } struct snd_soc_rbtree_node { - struct rb_node node; - unsigned int reg; - unsigned int value; - unsigned int defval; + struct rb_node node; /* the actual rbtree node holding this block */ + unsigned int base_reg; /* base register handled by this block */ + unsigned int word_size; /* number of bytes needed to represent the register index */ + void *block; /* block of adjacent registers */ + unsigned int blklen; /* number of registers available in the block */ } __attribute__ ((packed)); struct snd_soc_rbtree_ctx { struct rb_root root; + struct snd_soc_rbtree_node *cached_rbnode; }; +static inline void snd_soc_rbtree_get_base_top_reg( + struct snd_soc_rbtree_node *rbnode, + unsigned int *base, unsigned int *top) +{ + *base = rbnode->base_reg; + *top = rbnode->base_reg + rbnode->blklen - 1; +} + +static unsigned int snd_soc_rbtree_get_register( + struct snd_soc_rbtree_node *rbnode, unsigned int idx) +{ + unsigned int val; + + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + val = p[idx]; + return val; + } + case 2: { + u16 *p = rbnode->block; + val = p[idx]; + return val; + } + default: + BUG(); + break; + } + return -1; +} + +static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode, + unsigned int idx, unsigned int val) +{ + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + p[idx] = val; + break; + } + case 2: { + u16 *p = rbnode->block; + p[idx] = val; + break; + } + default: + BUG(); + break; + } +} + static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup( struct rb_root *root, unsigned int reg) { struct rb_node *node; struct snd_soc_rbtree_node *rbnode; + unsigned int base_reg, top_reg; node = root->rb_node; while (node) { rbnode = container_of(node, struct snd_soc_rbtree_node, node); - if (rbnode->reg < reg) - node = node->rb_left; - else if (rbnode->reg > reg) - node = node->rb_right; - else + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) return rbnode; + else if (reg > top_reg) + node = node->rb_right; + else if (reg < base_reg) + node = node->rb_left; } return NULL; @@ -518,19 +157,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root, { struct rb_node **new, *parent; struct snd_soc_rbtree_node *rbnode_tmp; + unsigned int base_reg_tmp, top_reg_tmp; + unsigned int base_reg; parent = NULL; new = &root->rb_node; while (*new) { rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node, node); + /* base and top registers of the current rbnode */ + snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + &top_reg_tmp); + /* base register of the rbnode to be added */ + base_reg = rbnode->base_reg; parent = *new; - if (rbnode_tmp->reg < rbnode->reg) - new = &((*new)->rb_left); - else if (rbnode_tmp->reg > rbnode->reg) - new = &((*new)->rb_right); - else + /* if this register has already been inserted, just return */ + if (base_reg >= base_reg_tmp && + base_reg <= top_reg_tmp) return 0; + else if (base_reg > top_reg_tmp) + new = &((*new)->rb_right); + else if (base_reg < base_reg_tmp) + new = &((*new)->rb_left); } /* insert the node into the rbtree */ @@ -545,58 +193,146 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) struct snd_soc_rbtree_ctx *rbtree_ctx; struct rb_node *node; struct snd_soc_rbtree_node *rbnode; - unsigned int val; + unsigned int regtmp; + unsigned int val, def; int ret; + int i; rbtree_ctx = codec->reg_cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); - if (rbnode->value == rbnode->defval) - continue; - WARN_ON(codec->writable_register && - codec->writable_register(codec, rbnode->reg)); - ret = snd_soc_cache_read(codec, rbnode->reg, &val); - if (ret) - return ret; - codec->cache_bypass = 1; - ret = snd_soc_write(codec, rbnode->reg, val); - codec->cache_bypass = 0; - if (ret) - return ret; - dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", - rbnode->reg, val); + for (i = 0; i < rbnode->blklen; ++i) { + regtmp = rbnode->base_reg + i; + WARN_ON(codec->writable_register && + codec->writable_register(codec, regtmp)); + val = snd_soc_rbtree_get_register(rbnode, i); + def = snd_soc_get_cache_val(codec->reg_def_copy, i, + rbnode->word_size); + if (val == def) + continue; + + codec->cache_bypass = 1; + ret = snd_soc_write(codec, regtmp, val); + codec->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", + regtmp, val); + } } return 0; } +static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode, + unsigned int pos, unsigned int reg, + unsigned int value) +{ + u8 *blk; + + blk = krealloc(rbnode->block, + (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL); + if (!blk) + return -ENOMEM; + + /* insert the register value in the correct place in the rbnode block */ + memmove(blk + (pos + 1) * rbnode->word_size, + blk + pos * rbnode->word_size, + (rbnode->blklen - pos) * rbnode->word_size); + + /* update the rbnode block, its size and the base register */ + rbnode->block = blk; + rbnode->blklen++; + if (!pos) + rbnode->base_reg = reg; + + snd_soc_rbtree_set_register(rbnode, pos, value); + return 0; +} + static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { struct snd_soc_rbtree_ctx *rbtree_ctx; - struct snd_soc_rbtree_node *rbnode; + struct snd_soc_rbtree_node *rbnode, *rbnode_tmp; + struct rb_node *node; + unsigned int val; + unsigned int reg_tmp; + unsigned int base_reg, top_reg; + unsigned int pos; + int i; + int ret; rbtree_ctx = codec->reg_cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + val = snd_soc_rbtree_get_register(rbnode, reg_tmp); + if (val == value) + return 0; + snd_soc_rbtree_set_register(rbnode, reg_tmp, value); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { - if (rbnode->value == value) + reg_tmp = reg - rbnode->base_reg; + val = snd_soc_rbtree_get_register(rbnode, reg_tmp); + if (val == value) return 0; - rbnode->value = value; + snd_soc_rbtree_set_register(rbnode, reg_tmp, value); + rbtree_ctx->cached_rbnode = rbnode; } else { /* bail out early, no need to create the rbnode yet */ if (!value) return 0; - /* - * for uninitialized registers whose value is changed - * from the default zero, create an rbnode and insert - * it into the tree. + /* look for an adjacent register to the one we are about to add */ + for (node = rb_first(&rbtree_ctx->root); node; + node = rb_next(node)) { + rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node); + for (i = 0; i < rbnode_tmp->blklen; ++i) { + reg_tmp = rbnode_tmp->base_reg + i; + if (abs(reg_tmp - reg) != 1) + continue; + /* decide where in the block to place our register */ + if (reg_tmp + 1 == reg) + pos = i + 1; + else + pos = i; + ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos, + reg, value); + if (ret) + return ret; + rbtree_ctx->cached_rbnode = rbnode_tmp; + return 0; + } + } + /* we did not manage to find a place to insert it in an existing + * block so create a new rbnode with a single register in its block. + * This block will get populated further if any other adjacent + * registers get modified in the future. */ rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); if (!rbnode) return -ENOMEM; - rbnode->reg = reg; - rbnode->value = value; + rbnode->blklen = 1; + rbnode->base_reg = reg; + rbnode->word_size = codec->driver->reg_word_size; + rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size, + GFP_KERNEL); + if (!rbnode->block) { + kfree(rbnode); + return -ENOMEM; + } + snd_soc_rbtree_set_register(rbnode, 0, value); snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); + rbtree_ctx->cached_rbnode = rbnode; } return 0; @@ -607,11 +343,28 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec, { struct snd_soc_rbtree_ctx *rbtree_ctx; struct snd_soc_rbtree_node *rbnode; + unsigned int base_reg, top_reg; + unsigned int reg_tmp; rbtree_ctx = codec->reg_cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { - *value = rbnode->value; + reg_tmp = reg - rbnode->base_reg; + *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); + rbtree_ctx->cached_rbnode = rbnode; } else { /* uninitialized registers default to 0 */ *value = 0; @@ -637,6 +390,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node); next = rb_next(&rbtree_node->node); rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node->block); kfree(rbtree_node); } @@ -649,10 +403,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) { - struct snd_soc_rbtree_node *rbtree_node; struct snd_soc_rbtree_ctx *rbtree_ctx; - unsigned int val; unsigned int word_size; + unsigned int val; int i; int ret; @@ -662,32 +415,27 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) rbtree_ctx = codec->reg_cache; rbtree_ctx->root = RB_ROOT; + rbtree_ctx->cached_rbnode = NULL; if (!codec->reg_def_copy) return 0; - /* - * populate the rbtree with the initialized registers. All other - * registers will be inserted when they are first modified. - */ word_size = codec->driver->reg_word_size; for (i = 0; i < codec->driver->reg_cache_size; ++i) { - val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); + val = snd_soc_get_cache_val(codec->reg_def_copy, i, + word_size); if (!val) continue; - rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); - if (!rbtree_node) { - ret = -ENOMEM; - snd_soc_cache_exit(codec); - break; - } - rbtree_node->reg = i; - rbtree_node->value = val; - rbtree_node->defval = val; - snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); + ret = snd_soc_rbtree_cache_write(codec, i, val); + if (ret) + goto err; } return 0; + +err: + snd_soc_cache_exit(codec); + return ret; } #ifdef CONFIG_SND_SOC_CACHE_LZO diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b194be09e74d..83ad8ca27490 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -44,7 +44,6 @@ #define NAME_SIZE 32 -static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS @@ -58,7 +57,7 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); -static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); +int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* * This is a timeout to do a DAPM powerdown after a stream is closed(). @@ -485,552 +484,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) } #endif -static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - if (!codec_dai->driver->symmetric_rates && - !cpu_dai->driver->symmetric_rates && - !rtd->dai_link->symmetric_rates) - return 0; - - /* This can happen if multiple streams are starting simultaneously - - * the second can need to get its constraints before the first has - * picked a rate. Complain and allow the application to carry on. - */ - if (!rtd->rate) { - dev_warn(&rtd->dev, - "Not enforcing symmetric_rates due to race\n"); - return 0; - } - - dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); - - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - rtd->rate, rtd->rate); - if (ret < 0) { - dev_err(&rtd->dev, - "Unable to apply rate symmetry constraint: %d\n", ret); - return ret; - } - - return 0; -} - -/* - * Called by ALSA when a PCM substream is opened, the runtime->hw record is - * then initialized and any private data can be allocated. This also calls - * startup for the cpu DAI, platform, machine and codec DAI. - */ -static int soc_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; - int ret = 0; - - mutex_lock(&pcm_mutex); - - /* startup the audio subsystem */ - if (cpu_dai->driver->ops->startup) { - ret = cpu_dai->driver->ops->startup(substream, cpu_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: can't open interface %s\n", - cpu_dai->name); - goto out; - } - } - - if (platform->driver->ops && platform->driver->ops->open) { - ret = platform->driver->ops->open(substream); - if (ret < 0) { - printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); - goto platform_err; - } - } - - if (codec_dai->driver->ops->startup) { - ret = codec_dai->driver->ops->startup(substream, codec_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: can't open codec %s\n", - codec_dai->name); - goto codec_dai_err; - } - } - - if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { - ret = rtd->dai_link->ops->startup(substream); - if (ret < 0) { - printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name); - goto machine_err; - } - } - - /* Check that the codec and cpu DAIs are compatible */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - runtime->hw.rate_min = - max(codec_dai_drv->playback.rate_min, - cpu_dai_drv->playback.rate_min); - runtime->hw.rate_max = - min(codec_dai_drv->playback.rate_max, - cpu_dai_drv->playback.rate_max); - runtime->hw.channels_min = - max(codec_dai_drv->playback.channels_min, - cpu_dai_drv->playback.channels_min); - runtime->hw.channels_max = - min(codec_dai_drv->playback.channels_max, - cpu_dai_drv->playback.channels_max); - runtime->hw.formats = - codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; - runtime->hw.rates = - codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; - if (codec_dai_drv->playback.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= cpu_dai_drv->playback.rates; - if (cpu_dai_drv->playback.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= codec_dai_drv->playback.rates; - } else { - runtime->hw.rate_min = - max(codec_dai_drv->capture.rate_min, - cpu_dai_drv->capture.rate_min); - runtime->hw.rate_max = - min(codec_dai_drv->capture.rate_max, - cpu_dai_drv->capture.rate_max); - runtime->hw.channels_min = - max(codec_dai_drv->capture.channels_min, - cpu_dai_drv->capture.channels_min); - runtime->hw.channels_max = - min(codec_dai_drv->capture.channels_max, - cpu_dai_drv->capture.channels_max); - runtime->hw.formats = - codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; - runtime->hw.rates = - codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; - if (codec_dai_drv->capture.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= cpu_dai_drv->capture.rates; - if (cpu_dai_drv->capture.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= codec_dai_drv->capture.rates; - } - - ret = -EINVAL; - snd_pcm_limit_hw_rates(runtime); - if (!runtime->hw.rates) { - printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", - codec_dai->name, cpu_dai->name); - goto config_err; - } - if (!runtime->hw.formats) { - printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", - codec_dai->name, cpu_dai->name); - goto config_err; - } - if (!runtime->hw.channels_min || !runtime->hw.channels_max || - runtime->hw.channels_min > runtime->hw.channels_max) { - printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", - codec_dai->name, cpu_dai->name); - goto config_err; - } - - /* Symmetry only applies if we've already got an active stream. */ - if (cpu_dai->active || codec_dai->active) { - ret = soc_pcm_apply_symmetry(substream); - if (ret != 0) - goto config_err; - } - - pr_debug("asoc: %s <-> %s info:\n", - codec_dai->name, cpu_dai->name); - pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); - pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, - runtime->hw.channels_max); - pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, - runtime->hw.rate_max); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - cpu_dai->active++; - codec_dai->active++; - rtd->codec->active++; - mutex_unlock(&pcm_mutex); - return 0; - -config_err: - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) - rtd->dai_link->ops->shutdown(substream); - -machine_err: - if (codec_dai->driver->ops->shutdown) - codec_dai->driver->ops->shutdown(substream, codec_dai); - -codec_dai_err: - if (platform->driver->ops && platform->driver->ops->close) - platform->driver->ops->close(substream); - -platform_err: - if (cpu_dai->driver->ops->shutdown) - cpu_dai->driver->ops->shutdown(substream, cpu_dai); -out: - mutex_unlock(&pcm_mutex); - return ret; -} - -/* - * Power down the audio subsystem pmdown_time msecs after close is called. - * This is to ensure there are no pops or clicks in between any music tracks - * due to DAPM power cycling. - */ -static void close_delayed_work(struct work_struct *work) -{ - struct snd_soc_pcm_runtime *rtd = - container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - mutex_lock(&pcm_mutex); - - pr_debug("pop wq checking: %s status: %s waiting: %s\n", - codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", - codec_dai->pop_wait ? "yes" : "no"); - - /* are we waiting on this codec DAI stream */ - if (codec_dai->pop_wait == 1) { - codec_dai->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->playback.stream_name, - SND_SOC_DAPM_STREAM_STOP); - } - - mutex_unlock(&pcm_mutex); -} - -/* - * Called by ALSA when a PCM substream is closed. Private data can be - * freed here. The cpu DAI, codec DAI, machine and platform are also - * shutdown. - */ -static int soc_codec_close(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = rtd->codec; - - mutex_lock(&pcm_mutex); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - codec_dai->capture_active--; - } - - cpu_dai->active--; - codec_dai->active--; - codec->active--; - - /* Muting the DAC suppresses artifacts caused during digital - * shutdown, for example from stopping clocks. - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_digital_mute(codec_dai, 1); - - if (cpu_dai->driver->ops->shutdown) - cpu_dai->driver->ops->shutdown(substream, cpu_dai); - - if (codec_dai->driver->ops->shutdown) - codec_dai->driver->ops->shutdown(substream, codec_dai); - - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) - rtd->dai_link->ops->shutdown(substream); - - if (platform->driver->ops && platform->driver->ops->close) - platform->driver->ops->close(substream); - cpu_dai->runtime = NULL; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* start delayed pop wq here for playback streams */ - codec_dai->pop_wait = 1; - schedule_delayed_work(&rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); - } else { - /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->capture.stream_name, - SND_SOC_DAPM_STREAM_STOP); - } - - mutex_unlock(&pcm_mutex); - return 0; -} - -/* - * Called by ALSA when the PCM substream is prepared, can set format, sample - * rate, etc. This function is non atomic and can be called multiple times, - * it can refer to the runtime info. - */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret = 0; - - mutex_lock(&pcm_mutex); - - if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { - ret = rtd->dai_link->ops->prepare(substream); - if (ret < 0) { - printk(KERN_ERR "asoc: machine prepare error\n"); - goto out; - } - } - - if (platform->driver->ops && platform->driver->ops->prepare) { - ret = platform->driver->ops->prepare(substream); - if (ret < 0) { - printk(KERN_ERR "asoc: platform prepare error\n"); - goto out; - } - } - - if (codec_dai->driver->ops->prepare) { - ret = codec_dai->driver->ops->prepare(substream, codec_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: codec DAI prepare error\n"); - goto out; - } - } - - if (cpu_dai->driver->ops->prepare) { - ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: cpu DAI prepare error\n"); - goto out; - } - } - - /* cancel any delayed stream shutdown that is pending */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - codec_dai->pop_wait) { - codec_dai->pop_wait = 0; - cancel_delayed_work(&rtd->delayed_work); - } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->playback.stream_name, - SND_SOC_DAPM_STREAM_START); - else - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->capture.stream_name, - SND_SOC_DAPM_STREAM_START); - - snd_soc_dai_digital_mute(codec_dai, 0); - -out: - mutex_unlock(&pcm_mutex); - return ret; -} - -/* - * Called by ALSA when the hardware params are set by application. This - * function can also be called multiple times and can allocate buffers - * (using snd_pcm_lib_* ). It's non-atomic. - */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret = 0; - - mutex_lock(&pcm_mutex); - - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { - ret = rtd->dai_link->ops->hw_params(substream, params); - if (ret < 0) { - printk(KERN_ERR "asoc: machine hw_params failed\n"); - goto out; - } - } - - if (codec_dai->driver->ops->hw_params) { - ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: can't set codec %s hw params\n", - codec_dai->name); - goto codec_err; - } - } - - if (cpu_dai->driver->ops->hw_params) { - ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); - if (ret < 0) { - printk(KERN_ERR "asoc: interface %s hw params failed\n", - cpu_dai->name); - goto interface_err; - } - } - - if (platform->driver->ops && platform->driver->ops->hw_params) { - ret = platform->driver->ops->hw_params(substream, params); - if (ret < 0) { - printk(KERN_ERR "asoc: platform %s hw params failed\n", - platform->name); - goto platform_err; - } - } - - rtd->rate = params_rate(params); - -out: - mutex_unlock(&pcm_mutex); - return ret; - -platform_err: - if (cpu_dai->driver->ops->hw_free) - cpu_dai->driver->ops->hw_free(substream, cpu_dai); - -interface_err: - if (codec_dai->driver->ops->hw_free) - codec_dai->driver->ops->hw_free(substream, codec_dai); - -codec_err: - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) - rtd->dai_link->ops->hw_free(substream); - - mutex_unlock(&pcm_mutex); - return ret; -} - -/* - * Frees resources allocated by hw_params, can be called multiple times - */ -static int soc_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = rtd->codec; - - mutex_lock(&pcm_mutex); - - /* apply codec digital mute */ - if (!codec->active) - snd_soc_dai_digital_mute(codec_dai, 1); - - /* free any machine hw params */ - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) - rtd->dai_link->ops->hw_free(substream); - - /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) - platform->driver->ops->hw_free(substream); - - /* now free hw params for the DAIs */ - if (codec_dai->driver->ops->hw_free) - codec_dai->driver->ops->hw_free(substream, codec_dai); - - if (cpu_dai->driver->ops->hw_free) - cpu_dai->driver->ops->hw_free(substream, cpu_dai); - - mutex_unlock(&pcm_mutex); - return 0; -} - -static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - if (codec_dai->driver->ops->trigger) { - ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); - if (ret < 0) - return ret; - } - - if (platform->driver->ops && platform->driver->ops->trigger) { - ret = platform->driver->ops->trigger(substream, cmd); - if (ret < 0) - return ret; - } - - if (cpu_dai->driver->ops->trigger) { - ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); - if (ret < 0) - return ret; - } - return 0; -} - -/* - * soc level wrapper for pointer callback - * If cpu_dai, codec_dai, platform driver has the delay callback, than - * the runtime->delay will be updated accordingly. - */ -static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t offset = 0; - snd_pcm_sframes_t delay = 0; - - if (platform->driver->ops && platform->driver->ops->pointer) - offset = platform->driver->ops->pointer(substream); - - if (cpu_dai->driver->ops->delay) - delay += cpu_dai->driver->ops->delay(substream, cpu_dai); - - if (codec_dai->driver->ops->delay) - delay += codec_dai->driver->ops->delay(substream, codec_dai); - - if (platform->driver->delay) - delay += platform->driver->delay(substream, codec_dai); - - runtime->delay = delay; - - return offset; -} - -/* ASoC PCM operations */ -static struct snd_pcm_ops soc_pcm_ops = { - .open = soc_pcm_open, - .close = soc_codec_close, - .hw_params = soc_pcm_hw_params, - .hw_free = soc_pcm_hw_free, - .prepare = soc_pcm_prepare, - .trigger = soc_pcm_trigger, - .pointer = soc_pcm_pointer, -}; - #ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ int snd_soc_suspend(struct device *dev) @@ -1124,6 +577,7 @@ int snd_soc_suspend(struct device *dev) case SND_SOC_BIAS_OFF: codec->driver->suspend(codec, PMSG_SUSPEND); codec->suspended = 1; + codec->cache_sync = 1; break; default: dev_dbg(codec->dev, "CODEC is on over suspend\n"); @@ -1256,7 +710,7 @@ static void soc_resume_deferred(struct work_struct *work) int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - int i; + int i, ac97_control = 0; /* AC97 devices might have other drivers hanging off them so * need to resume immediately. Other drivers don't have that @@ -1265,14 +719,15 @@ int snd_soc_resume(struct device *dev) */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - if (cpu_dai->driver->ac97_control) { - dev_dbg(dev, "Resuming AC97 immediately\n"); - soc_resume_deferred(&card->deferred_resume_work); - } else { - dev_dbg(dev, "Scheduling resume work\n"); - if (!schedule_work(&card->deferred_resume_work)) - dev_err(dev, "resume work item may be lost\n"); - } + ac97_control |= cpu_dai->driver->ac97_control; + } + if (ac97_control) { + dev_dbg(dev, "Resuming AC97 immediately\n"); + soc_resume_deferred(&card->deferred_resume_work); + } else { + dev_dbg(dev, "Scheduling resume work\n"); + if (!schedule_work(&card->deferred_resume_work)) + dev_err(dev, "resume work item may be lost\n"); } return 0; @@ -1393,7 +848,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec) module_put(codec->dev->driver->owner); } -static void soc_remove_dai_link(struct snd_soc_card *card, int num) +static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_codec *codec = rtd->codec; @@ -1410,7 +865,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } /* remove the CODEC DAI */ - if (codec_dai && codec_dai->probed) { + if (codec_dai && codec_dai->probed && + codec_dai->driver->remove_order == order) { if (codec_dai->driver->remove) { err = codec_dai->driver->remove(codec_dai); if (err < 0) @@ -1421,7 +877,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } /* remove the platform */ - if (platform && platform->probed) { + if (platform && platform->probed && + platform->driver->remove_order == order) { if (platform->driver->remove) { err = platform->driver->remove(platform); if (err < 0) @@ -1433,11 +890,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } /* remove the CODEC */ - if (codec && codec->probed) + if (codec && codec->probed && + codec->driver->remove_order == order) soc_remove_codec(codec); /* remove the cpu_dai */ - if (cpu_dai && cpu_dai->probed) { + if (cpu_dai && cpu_dai->probed && + cpu_dai->driver->remove_order == order) { if (cpu_dai->driver->remove) { err = cpu_dai->driver->remove(cpu_dai); if (err < 0) @@ -1451,11 +910,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) static void soc_remove_dai_links(struct snd_soc_card *card) { - int i; - - for (i = 0; i < card->num_rtd; i++) - soc_remove_dai_link(card, i); + int dai, order; + for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; + order++) { + for (dai = 0; dai < card->num_rtd; dai++) + soc_remove_dai_link(card, dai, order); + } card->num_rtd = 0; } @@ -1526,6 +987,52 @@ err_probe: return ret; } +static int soc_probe_platform(struct snd_soc_card *card, + struct snd_soc_platform *platform) +{ + int ret = 0; + const struct snd_soc_platform_driver *driver = platform->driver; + + platform->card = card; + platform->dapm.card = card; + + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&platform->dapm, + driver->dapm_widgets, driver->num_dapm_widgets); + + if (driver->probe) { + ret = driver->probe(platform); + if (ret < 0) { + dev_err(platform->dev, + "asoc: failed to probe platform %s: %d\n", + platform->name, ret); + goto err_probe; + } + } + + if (driver->controls) + snd_soc_add_platform_controls(platform, driver->controls, + driver->num_controls); + if (driver->dapm_routes) + snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes, + driver->num_dapm_routes); + + /* mark platform as probed and add to card platform list */ + platform->probed = 1; + list_add(&platform->card_list, &card->platform_dev_list); + list_add(&platform->dapm.list, &card->dapm_list); + + return 0; + +err_probe: + module_put(platform->dev->driver->owner); + + return ret; +} + static void rtd_release(struct device *dev) {} static int soc_post_component_init(struct snd_soc_card *card, @@ -1572,6 +1079,7 @@ static int soc_post_component_init(struct snd_soc_card *card, rtd->dev.parent = card->dev; rtd->dev.release = rtd_release; rtd->dev.init_name = name; + mutex_init(&rtd->pcm_mutex); ret = device_register(&rtd->dev); if (ret < 0) { dev_err(card->dev, @@ -1596,7 +1104,7 @@ static int soc_post_component_init(struct snd_soc_card *card, return 0; } -static int soc_probe_dai_link(struct snd_soc_card *card, int num) +static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; @@ -1605,7 +1113,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; int ret; - dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num); + dev_dbg(card->dev, "probe %s dai link %d late %d\n", + card->name, num, order); /* config components */ codec_dai->codec = codec; @@ -1617,7 +1126,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) rtd->pmdown_time = pmdown_time; /* probe the cpu_dai */ - if (!cpu_dai->probed) { + if (!cpu_dai->probed && + cpu_dai->driver->probe_order == order) { if (!try_module_get(cpu_dai->dev->driver->owner)) return -ENODEV; @@ -1631,38 +1141,28 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) } } cpu_dai->probed = 1; - /* mark cpu_dai as probed and add to card cpu_dai list */ + /* mark cpu_dai as probed and add to card dai list */ list_add(&cpu_dai->card_list, &card->dai_dev_list); } /* probe the CODEC */ - if (!codec->probed) { + if (!codec->probed && + codec->driver->probe_order == order) { ret = soc_probe_codec(card, codec); if (ret < 0) return ret; } /* probe the platform */ - if (!platform->probed) { - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - - if (platform->driver->probe) { - ret = platform->driver->probe(platform); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to probe platform %s\n", - platform->name); - module_put(platform->dev->driver->owner); - return ret; - } - } - /* mark platform as probed and add to card platform list */ - platform->probed = 1; - list_add(&platform->card_list, &card->platform_dev_list); + if (!platform->probed && + platform->driver->probe_order == order) { + ret = soc_probe_platform(card, platform); + if (ret < 0) + return ret; } /* probe the CODEC DAI */ - if (!codec_dai->probed) { + if (!codec_dai->probed && codec_dai->driver->probe_order == order) { if (codec_dai->driver->probe) { ret = codec_dai->driver->probe(codec_dai); if (ret < 0) { @@ -1672,13 +1172,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) } } - /* mark cpu_dai as probed and add to card cpu_dai list */ + /* mark codec_dai as probed and add to card dai list */ codec_dai->probed = 1; list_add(&codec_dai->card_list, &card->dai_dev_list); } - /* DAPM dai link stream work */ - INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + /* complete DAI probe during last probe */ + if (order != SND_SOC_COMP_ORDER_LAST) + return 0; ret = soc_post_component_init(card, codec, num, 0); if (ret) @@ -1817,7 +1318,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; - int ret, i; + int ret, i, order; mutex_lock(&card->mutex); @@ -1895,12 +1396,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) goto card_probe_error; } - for (i = 0; i < card->num_links; i++) { - ret = soc_probe_dai_link(card, i); - if (ret < 0) { - pr_err("asoc: failed to instantiate card %s: %d\n", + /* early DAI link probe */ + for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; + order++) { + for (i = 0; i < card->num_links; i++) { + ret = soc_probe_dai_link(card, i, order); + if (ret < 0) { + pr_err("asoc: failed to instantiate card %s: %d\n", card->name, ret); - goto probe_dai_err; + goto probe_dai_err; + } } } @@ -2096,67 +1601,6 @@ static struct platform_driver soc_driver = { .remove = soc_remove, }; -/* create a new pcm */ -static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_pcm *pcm; - char new_name[64]; - int ret = 0, playback = 0, capture = 0; - - /* check client and interface hw capabilities */ - snprintf(new_name, sizeof(new_name), "%s %s-%d", - rtd->dai_link->stream_name, codec_dai->name, num); - - if (codec_dai->driver->playback.channels_min) - playback = 1; - if (codec_dai->driver->capture.channels_min) - capture = 1; - - dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); - ret = snd_pcm_new(rtd->card->snd_card, new_name, - num, playback, capture, &pcm); - if (ret < 0) { - printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); - return ret; - } - - rtd->pcm = pcm; - pcm->private_data = rtd; - if (platform->driver->ops) { - soc_pcm_ops.mmap = platform->driver->ops->mmap; - soc_pcm_ops.pointer = platform->driver->ops->pointer; - soc_pcm_ops.ioctl = platform->driver->ops->ioctl; - soc_pcm_ops.copy = platform->driver->ops->copy; - soc_pcm_ops.silence = platform->driver->ops->silence; - soc_pcm_ops.ack = platform->driver->ops->ack; - soc_pcm_ops.page = platform->driver->ops->page; - } - - if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); - - if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); - - if (platform->driver->pcm_new) { - ret = platform->driver->pcm_new(rtd->card->snd_card, - codec_dai, pcm); - if (ret < 0) { - pr_err("asoc: platform pcm constructor failed\n"); - return ret; - } - } - - pcm->private_free = platform->driver->pcm_free; - printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, - cpu_dai->name); - return ret; -} - /** * snd_soc_codec_volatile_register: Report if a register is volatile. * @@ -2211,6 +1655,38 @@ int snd_soc_codec_writable_register(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register); +int snd_soc_platform_read(struct snd_soc_platform *platform, + unsigned int reg) +{ + unsigned int ret; + + if (!platform->driver->read) { + dev_err(platform->dev, "platform has no read back\n"); + return -1; + } + + ret = platform->driver->read(platform, reg); + dev_dbg(platform->dev, "read %x => %x\n", reg, ret); + trace_snd_soc_preg_read(platform, reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_platform_read); + +int snd_soc_platform_write(struct snd_soc_platform *platform, + unsigned int reg, unsigned int val) +{ + if (!platform->driver->write) { + dev_err(platform->dev, "platform has no write back\n"); + return -1; + } + + dev_dbg(platform->dev, "write %x = %x\n", reg, val); + trace_snd_soc_preg_write(platform, reg, val); + return platform->driver->write(platform, reg, val); +} +EXPORT_SYMBOL_GPL(snd_soc_platform_write); + /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec @@ -2323,7 +1799,7 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, return ret; old = ret; - new = (old & ~mask) | value; + new = (old & ~mask) | (value & mask); change = old != new; if (change) { ret = snd_soc_write(codec, reg, new); @@ -2490,6 +1966,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_add_controls); /** + * snd_soc_add_platform_controls - add an array of controls to a platform. + * Convienience function to add a list of controls. + * + * @platform: platform to add controls to + * @controls: array of controls to add + * @num_controls: number of elements in the array + * + * Return 0 for success, else error. + */ +int snd_soc_add_platform_controls(struct snd_soc_platform *platform, + const struct snd_kcontrol_new *controls, int num_controls) +{ + struct snd_card *card = platform->card->snd_card; + int err, i; + + for (i = 0; i < num_controls; i++) { + const struct snd_kcontrol_new *control = &controls[i]; + err = snd_ctl_add(card, snd_soc_cnew(control, platform, + control->name, NULL)); + if (err < 0) { + dev_err(platform->dev, "Failed to add %s %d\n",control->name, err); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); + +/** * snd_soc_info_enum_double - enumerated double mixer info callback * @kcontrol: mixer control * @uinfo: control element information @@ -3633,6 +3139,8 @@ int snd_soc_register_platform(struct device *dev, platform->dev = dev; platform->driver = platform_drv; + platform->dapm.dev = dev; + platform->dapm.platform = platform; mutex_lock(&client_mutex); list_add(&platform->list, &platform_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 32ab7fc4579a..7e15914b3633 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -124,6 +124,81 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +/* get snd_card from DAPM context */ +static inline struct snd_card *dapm_get_snd_card( + struct snd_soc_dapm_context *dapm) +{ + if (dapm->codec) + return dapm->codec->card->snd_card; + else if (dapm->platform) + return dapm->platform->card->snd_card; + else + BUG(); + + /* unreachable */ + return NULL; +} + +/* get soc_card from DAPM context */ +static inline struct snd_soc_card *dapm_get_soc_card( + struct snd_soc_dapm_context *dapm) +{ + if (dapm->codec) + return dapm->codec->card; + else if (dapm->platform) + return dapm->platform->card; + else + BUG(); + + /* unreachable */ + return NULL; +} + +static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) +{ + if (w->codec) + return snd_soc_read(w->codec, reg); + else if (w->platform) + return snd_soc_platform_read(w->platform, reg); + + dev_err(w->dapm->dev, "no valid widget read method\n"); + return -1; +} + +static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val) +{ + if (w->codec) + return snd_soc_write(w->codec, reg, val); + else if (w->platform) + return snd_soc_platform_write(w->platform, reg, val); + + dev_err(w->dapm->dev, "no valid widget write method\n"); + return -1; +} + +static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, + unsigned short reg, unsigned int mask, unsigned int value) +{ + int change; + unsigned int old, new; + int ret; + + ret = soc_widget_read(w, reg); + if (ret < 0) + return ret; + + old = ret; + new = (old & ~mask) | (value & mask); + change = old != new; + if (change) { + ret = soc_widget_write(w, reg, new); + if (ret < 0) + return ret; + } + + return change; +} + /** * snd_soc_dapm_set_bias_level - set the bias level for the system * @dapm: DAPM context @@ -139,39 +214,26 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card = dapm->card; int ret = 0; - switch (level) { - case SND_SOC_BIAS_ON: - dev_dbg(dapm->dev, "Setting full bias\n"); - break; - case SND_SOC_BIAS_PREPARE: - dev_dbg(dapm->dev, "Setting bias prepare\n"); - break; - case SND_SOC_BIAS_STANDBY: - dev_dbg(dapm->dev, "Setting standby bias\n"); - break; - case SND_SOC_BIAS_OFF: - dev_dbg(dapm->dev, "Setting bias off\n"); - break; - default: - dev_err(dapm->dev, "Setting invalid bias %d\n", level); - return -EINVAL; - } - trace_snd_soc_bias_level_start(card, level); if (card && card->set_bias_level) - ret = card->set_bias_level(card, level); - if (ret == 0) { - if (dapm->codec && dapm->codec->driver->set_bias_level) - ret = dapm->codec->driver->set_bias_level(dapm->codec, level); + ret = card->set_bias_level(card, dapm, level); + if (ret != 0) + goto out; + + if (dapm->codec) { + if (dapm->codec->driver->set_bias_level) + ret = dapm->codec->driver->set_bias_level(dapm->codec, + level); else dapm->bias_level = level; } - if (ret == 0) { - if (card && card->set_bias_level_post) - ret = card->set_bias_level_post(card, level); - } + if (ret != 0) + goto out; + if (card && card->set_bias_level_post) + ret = card->set_bias_level_post(card, dapm, level); +out: trace_snd_soc_bias_level_done(card, level); return ret; @@ -194,7 +256,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = snd_soc_read(w->codec, reg); + val = soc_widget_read(w, reg); val = (val >> shift) & mask; if ((invert && !val) || (!invert && val)) @@ -209,8 +271,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, int val, item, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) - ; - val = snd_soc_read(w->codec, e->reg); + ; + val = soc_widget_read(w, e->reg); item = (val >> e->shift_l) & (bitmask - 1); p->connect = 0; @@ -240,7 +302,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = snd_soc_read(w->codec, e->reg); + val = soc_widget_read(w, e->reg); val = (val >> e->shift_l) & e->mask; for (item = 0; item < e->max; item++) { if (val == e->values[item]) @@ -606,6 +668,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sinks, list_source) { + if (path->weak) + continue; + if (path->walked) continue; @@ -656,6 +721,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sources, list_sink) { + if (path->weak) + continue; + if (path->walked) continue; @@ -681,7 +749,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, else val = w->off_val; - snd_soc_update_bits(w->codec, -(w->reg + 1), + soc_widget_update_bits(w, -(w->reg + 1), w->mask << w->shift, val << w->shift); return 0; @@ -737,6 +805,9 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) /* Check if one of our outputs is connected */ list_for_each_entry(path, &w->sinks, list_source) { + if (path->weak) + continue; + if (path->connected && !path->connected(path->source, path->sink)) continue; @@ -885,11 +956,17 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, } if (reg >= 0) { + /* Any widget will do, they should all be updating the + * same register. + */ + w = list_first_entry(pending, struct snd_soc_dapm_widget, + power_list); + pop_dbg(dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - snd_soc_update_bits(dapm->codec, reg, mask, value); + soc_widget_update_bits(w, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -942,7 +1019,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&pending); cur_sort = -1; - cur_subseq = -1; + cur_subseq = INT_MIN; cur_reg = SND_SOC_NOPM; cur_dapm = NULL; } @@ -1041,16 +1118,17 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) struct snd_soc_dapm_context *d = data; int ret; - if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + /* If we're off and we're not supposed to be go into STANDBY */ + if (d->bias_level == SND_SOC_BIAS_OFF && + d->target_bias_level != SND_SOC_BIAS_OFF) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); if (ret != 0) dev_err(d->dev, "Failed to turn on bias: %d\n", ret); } - /* If we're changing to all on or all off then prepare */ - if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || - (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + /* Prepare for a STADDBY->ON or ON->STANDBY transition */ + if (d->bias_level != d->target_bias_level) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); if (ret != 0) dev_err(d->dev, @@ -1067,7 +1145,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) int ret; /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + if (d->bias_level == SND_SOC_BIAS_PREPARE && + (d->target_bias_level == SND_SOC_BIAS_STANDBY || + d->target_bias_level == SND_SOC_BIAS_OFF)) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); if (ret != 0) dev_err(d->dev, "Failed to apply standby bias: %d\n", @@ -1075,14 +1155,16 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) } /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { + if (d->bias_level == SND_SOC_BIAS_STANDBY && + d->target_bias_level == SND_SOC_BIAS_OFF) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); if (ret != 0) dev_err(d->dev, "Failed to turn off bias: %d\n", ret); } /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + if (d->bias_level == SND_SOC_BIAS_PREPARE && + d->target_bias_level == SND_SOC_BIAS_ON) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); if (ret != 0) dev_err(d->dev, "Failed to apply active bias: %d\n", @@ -1107,13 +1189,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) LIST_HEAD(up_list); LIST_HEAD(down_list); LIST_HEAD(async_domain); + enum snd_soc_bias_level bias; int power; trace_snd_soc_dapm_start(card); - list_for_each_entry(d, &card->dapm_list, list) - if (d->n_widgets || d->codec == NULL) - d->dev_power = 0; + list_for_each_entry(d, &card->dapm_list, list) { + if (d->n_widgets || d->codec == NULL) { + if (d->idle_bias_off) + d->target_bias_level = SND_SOC_BIAS_OFF; + else + d->target_bias_level = SND_SOC_BIAS_STANDBY; + } + } /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. @@ -1135,8 +1223,27 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) power = w->power_check(w); else power = 1; - if (power) - w->dapm->dev_power = 1; + + if (power) { + d = w->dapm; + + /* Supplies and micbiases only bring + * the context up to STANDBY as unless + * something else is active and + * passing audio they generally don't + * require full power. + */ + switch (w->id) { + case snd_soc_dapm_supply: + case snd_soc_dapm_micbias: + if (d->target_bias_level < SND_SOC_BIAS_STANDBY) + d->target_bias_level = SND_SOC_BIAS_STANDBY; + break; + default: + d->target_bias_level = SND_SOC_BIAS_ON; + break; + } + } if (w->power == power) continue; @@ -1160,24 +1267,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) switch (event) { case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_RESUME: - dapm->dev_power = 1; + dapm->target_bias_level = SND_SOC_BIAS_ON; break; case SND_SOC_DAPM_STREAM_STOP: - dapm->dev_power = !!dapm->codec->active; + if (dapm->codec->active) + dapm->target_bias_level = SND_SOC_BIAS_ON; + else + dapm->target_bias_level = SND_SOC_BIAS_STANDBY; break; case SND_SOC_DAPM_STREAM_SUSPEND: - dapm->dev_power = 0; + dapm->target_bias_level = SND_SOC_BIAS_STANDBY; break; case SND_SOC_DAPM_STREAM_NOP: - switch (dapm->bias_level) { - case SND_SOC_BIAS_STANDBY: - case SND_SOC_BIAS_OFF: - dapm->dev_power = 0; - break; - default: - dapm->dev_power = 1; - break; - } + dapm->target_bias_level = dapm->bias_level; break; default: break; @@ -1185,12 +1287,12 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } /* Force all contexts in the card to the same bias state */ - power = 0; + bias = SND_SOC_BIAS_OFF; list_for_each_entry(d, &card->dapm_list, list) - if (d->dev_power) - power = 1; + if (d->target_bias_level > bias) + bias = d->target_bias_level; list_for_each_entry(d, &card->dapm_list, list) - d->dev_power = power; + d->target_bias_level = bias; /* Run all the bias changes in parallel */ @@ -1794,6 +1896,84 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); +static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, + route->source, + true); + struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, + route->sink, + true); + struct snd_soc_dapm_path *path; + int count = 0; + + if (!source) { + dev_err(dapm->dev, "Unable to find source %s for weak route\n", + route->source); + return -ENODEV; + } + + if (!sink) { + dev_err(dapm->dev, "Unable to find sink %s for weak route\n", + route->sink); + return -ENODEV; + } + + if (route->control || route->connected) + dev_warn(dapm->dev, "Ignoring control for weak route %s->%s\n", + route->source, route->sink); + + list_for_each_entry(path, &source->sinks, list_source) { + if (path->sink == sink) { + path->weak = 1; + count++; + } + } + + if (count == 0) + dev_err(dapm->dev, "No path found for weak route %s->%s\n", + route->source, route->sink); + if (count > 1) + dev_warn(dapm->dev, "%d paths found for weak route %s->%s\n", + count, route->source, route->sink); + + return 0; +} + +/** + * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak + * @dapm: DAPM context + * @route: audio routes + * @num: number of routes + * + * Mark existing routes matching those specified in the passed array + * as being weak, meaning that they are ignored for the purpose of + * power decisions. The main intended use case is for sidetone paths + * which couple audio between other independent paths if they are both + * active in order to make the combination work better at the user + * level but which aren't intended to be "used". + * + * Note that CODEC drivers should not use this as sidetone type paths + * can frequently also be used as bypass paths. + */ +int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num) +{ + int i, err; + int ret = 0; + + for (i = 0; i < num; i++) { + err = snd_soc_dapm_weak_route(dapm, route); + if (err) + ret = err; + route++; + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); + /** * snd_soc_dapm_new_widgets - add new dapm widgets * @dapm: DAPM context @@ -1865,7 +2045,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = snd_soc_read(w->codec, w->reg); + val = soc_widget_read(w, w->reg); val &= 1 << w->shift; if (w->invert) val = !val; @@ -2353,6 +2533,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; + w->platform = dapm->platform; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c new file mode 100644 index 000000000000..cca490c80589 --- /dev/null +++ b/sound/soc/soc-io.c @@ -0,0 +1,396 @@ +/* + * soc-io.c -- ASoC register I/O helpers + * + * Copyright 2009-2011 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include <trace/events/asoc.h> + +#ifdef CONFIG_SPI_MASTER +static int do_spi_write(void *control, const char *data, int len) +{ + struct spi_device *spi = control; + int ret; + + ret = spi_write(spi, data, len); + if (ret < 0) + return ret; + + return len; +} +#endif + +static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value, const void *data, int len) +{ + int ret; + + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } + + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } + + ret = codec->hw_write(codec->control_data, data, len); + if (ret == len) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + +static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) +{ + int ret; + unsigned int val; + + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; +} + +static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data; + + data = cpu_to_be16((reg << 12) | (value & 0xffffff)); + + return do_hw_write(codec, reg, value, &data, 2); +} + +static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data; + + data = cpu_to_be16((reg << 9) | (value & 0x1ff)); + + return do_hw_write(codec, reg, value, &data, 2); +} + +static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + reg &= 0xff; + data[0] = reg; + data[1] = value & 0xff; + + return do_hw_write(codec, reg, value, data, 2); +} + +static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + u16 val = cpu_to_be16(value); + + data[0] = reg; + memcpy(&data[1], &val, sizeof(val)); + + return do_hw_write(codec, reg, value, data, 3); +} + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int do_i2c_read(struct snd_soc_codec *codec, + void *reg, int reglen, + void *data, int datalen) +{ + struct i2c_msg xfer[2]; + int ret; + struct i2c_client *client = codec->control_data; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = reglen; + xfer[0].buf = reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = datalen; + xfer[1].buf = data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret == 2) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u8 reg = r; + u8 data; + int ret; + + ret = do_i2c_read(codec, ®, 1, &data, 1); + if (ret < 0) + return 0; + return data; +} +#else +#define snd_soc_8_8_read_i2c NULL +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u8 reg = r; + u16 data; + int ret; + + ret = do_i2c_read(codec, ®, 1, &data, 2); + if (ret < 0) + return 0; + return (data >> 8) | ((data & 0xff) << 8); +} +#else +#define snd_soc_8_16_read_i2c NULL +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u16 reg = r; + u8 data; + int ret; + + ret = do_i2c_read(codec, ®, 2, &data, 1); + if (ret < 0) + return 0; + return data; +} +#else +#define snd_soc_16_8_read_i2c NULL +#endif + +static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + u16 rval = cpu_to_be16(reg); + + memcpy(data, &rval, sizeof(rval)); + data[2] = value; + + return do_hw_write(codec, reg, value, data, 3); +} + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u16 reg = cpu_to_be16(r); + u16 data; + int ret; + + ret = do_i2c_read(codec, ®, 2, &data, 2); + if (ret < 0) + return 0; + return be16_to_cpu(data); +} +#else +#define snd_soc_16_16_read_i2c NULL +#endif + +static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data[2]; + + data[0] = cpu_to_be16(reg); + data[1] = cpu_to_be16(value); + + return do_hw_write(codec, reg, value, data, sizeof(data)); +} + +/* Primitive bulk write support for soc-cache. The data pointed to by + * `data' needs to already be in the form the hardware expects + * including any leading register specific data. Any data written + * through this function will not go through the cache as it only + * handles writing to volatile or out of bounds registers. + */ +static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, + const void *data, size_t len) +{ + int ret; + + /* To ensure that we don't get out of sync with the cache, check + * whether the base register is volatile or if we've directly asked + * to bypass the cache. Out of bounds registers are considered + * volatile. + */ + if (!codec->cache_bypass + && !snd_soc_codec_volatile_register(codec, reg) + && reg < codec->driver->reg_cache_size) + return -EINVAL; + + switch (codec->control_type) { +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + case SND_SOC_I2C: + ret = i2c_master_send(to_i2c_client(codec->dev), data, len); + break; +#endif +#if defined(CONFIG_SPI_MASTER) + case SND_SOC_SPI: + ret = spi_write(to_spi_device(codec->dev), data, len); + break; +#endif + default: + BUG(); + } + + if (ret == len) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + +static struct { + int addr_bits; + int data_bits; + int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); + unsigned int (*read)(struct snd_soc_codec *, unsigned int); + unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); +} io_types[] = { + { + .addr_bits = 4, .data_bits = 12, + .write = snd_soc_4_12_write, + }, + { + .addr_bits = 7, .data_bits = 9, + .write = snd_soc_7_9_write, + }, + { + .addr_bits = 8, .data_bits = 8, + .write = snd_soc_8_8_write, + .i2c_read = snd_soc_8_8_read_i2c, + }, + { + .addr_bits = 8, .data_bits = 16, + .write = snd_soc_8_16_write, + .i2c_read = snd_soc_8_16_read_i2c, + }, + { + .addr_bits = 16, .data_bits = 8, + .write = snd_soc_16_8_write, + .i2c_read = snd_soc_16_8_read_i2c, + }, + { + .addr_bits = 16, .data_bits = 16, + .write = snd_soc_16_16_write, + .i2c_read = snd_soc_16_16_read_i2c, + }, +}; + +/** + * snd_soc_codec_set_cache_io: Set up standard I/O functions. + * + * @codec: CODEC to configure. + * @addr_bits: Number of bits of register address data. + * @data_bits: Number of bits of data per register. + * @control: Control bus used. + * + * Register formats are frequently shared between many I2C and SPI + * devices. In order to promote code reuse the ASoC core provides + * some standard implementations of CODEC read and write operations + * which can be set up using this function. + * + * The caller is responsible for allocating and initialising the + * actual cache. + * + * Note that at present this code cannot be used by CODECs with + * volatile registers. + */ +int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, + int addr_bits, int data_bits, + enum snd_soc_control_type control) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(io_types); i++) + if (io_types[i].addr_bits == addr_bits && + io_types[i].data_bits == data_bits) + break; + if (i == ARRAY_SIZE(io_types)) { + printk(KERN_ERR + "No I/O functions for %d bit address %d bit data\n", + addr_bits, data_bits); + return -EINVAL; + } + + codec->write = io_types[i].write; + codec->read = hw_read; + codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; + + switch (control) { + case SND_SOC_I2C: +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + codec->hw_write = (hw_write_t)i2c_master_send; +#endif + if (io_types[i].i2c_read) + codec->hw_read = io_types[i].i2c_read; + + codec->control_data = container_of(codec->dev, + struct i2c_client, + dev); + break; + + case SND_SOC_SPI: +#ifdef CONFIG_SPI_MASTER + codec->hw_write = do_spi_write; +#endif + + codec->control_data = container_of(codec->dev, + struct spi_device, + dev); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); + diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c new file mode 100644 index 000000000000..b5759397afa3 --- /dev/null +++ b/sound/soc/soc-pcm.c @@ -0,0 +1,639 @@ +/* + * soc-pcm.c -- ALSA SoC PCM + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * Copyright (C) 2010 Slimlogic Ltd. + * Copyright (C) 2010 Texas Instruments Inc. + * + * Authors: Liam Girdwood <lrg@ti.com> + * Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> + +static DEFINE_MUTEX(pcm_mutex); + +static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + if (!codec_dai->driver->symmetric_rates && + !cpu_dai->driver->symmetric_rates && + !rtd->dai_link->symmetric_rates) + return 0; + + /* This can happen if multiple streams are starting simultaneously - + * the second can need to get its constraints before the first has + * picked a rate. Complain and allow the application to carry on. + */ + if (!rtd->rate) { + dev_warn(&rtd->dev, + "Not enforcing symmetric_rates due to race\n"); + return 0; + } + + dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + rtd->rate, rtd->rate); + if (ret < 0) { + dev_err(&rtd->dev, + "Unable to apply rate symmetry constraint: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Called by ALSA when a PCM substream is opened, the runtime->hw record is + * then initialized and any private data can be allocated. This also calls + * startup for the cpu DAI, platform, machine and codec DAI. + */ +static int soc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + /* startup the audio subsystem */ + if (cpu_dai->driver->ops->startup) { + ret = cpu_dai->driver->ops->startup(substream, cpu_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open interface %s\n", + cpu_dai->name); + goto out; + } + } + + if (platform->driver->ops && platform->driver->ops->open) { + ret = platform->driver->ops->open(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); + goto platform_err; + } + } + + if (codec_dai->driver->ops->startup) { + ret = codec_dai->driver->ops->startup(substream, codec_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: can't open codec %s\n", + codec_dai->name); + goto codec_dai_err; + } + } + + if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { + ret = rtd->dai_link->ops->startup(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name); + goto machine_err; + } + } + + /* Check that the codec and cpu DAIs are compatible */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_min = + max(codec_dai_drv->playback.rate_min, + cpu_dai_drv->playback.rate_min); + runtime->hw.rate_max = + min(codec_dai_drv->playback.rate_max, + cpu_dai_drv->playback.rate_max); + runtime->hw.channels_min = + max(codec_dai_drv->playback.channels_min, + cpu_dai_drv->playback.channels_min); + runtime->hw.channels_max = + min(codec_dai_drv->playback.channels_max, + cpu_dai_drv->playback.channels_max); + runtime->hw.formats = + codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; + runtime->hw.rates = + codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; + if (codec_dai_drv->playback.rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + runtime->hw.rates |= cpu_dai_drv->playback.rates; + if (cpu_dai_drv->playback.rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + runtime->hw.rates |= codec_dai_drv->playback.rates; + } else { + runtime->hw.rate_min = + max(codec_dai_drv->capture.rate_min, + cpu_dai_drv->capture.rate_min); + runtime->hw.rate_max = + min(codec_dai_drv->capture.rate_max, + cpu_dai_drv->capture.rate_max); + runtime->hw.channels_min = + max(codec_dai_drv->capture.channels_min, + cpu_dai_drv->capture.channels_min); + runtime->hw.channels_max = + min(codec_dai_drv->capture.channels_max, + cpu_dai_drv->capture.channels_max); + runtime->hw.formats = + codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; + runtime->hw.rates = + codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; + if (codec_dai_drv->capture.rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + runtime->hw.rates |= cpu_dai_drv->capture.rates; + if (cpu_dai_drv->capture.rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + runtime->hw.rates |= codec_dai_drv->capture.rates; + } + + ret = -EINVAL; + snd_pcm_limit_hw_rates(runtime); + if (!runtime->hw.rates) { + printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", + codec_dai->name, cpu_dai->name); + goto config_err; + } + if (!runtime->hw.formats) { + printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", + codec_dai->name, cpu_dai->name); + goto config_err; + } + if (!runtime->hw.channels_min || !runtime->hw.channels_max || + runtime->hw.channels_min > runtime->hw.channels_max) { + printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", + codec_dai->name, cpu_dai->name); + goto config_err; + } + + /* Symmetry only applies if we've already got an active stream. */ + if (cpu_dai->active || codec_dai->active) { + ret = soc_pcm_apply_symmetry(substream); + if (ret != 0) + goto config_err; + } + + pr_debug("asoc: %s <-> %s info:\n", + codec_dai->name, cpu_dai->name); + pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); + pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, + runtime->hw.channels_max); + pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, + runtime->hw.rate_max); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active++; + codec_dai->playback_active++; + } else { + cpu_dai->capture_active++; + codec_dai->capture_active++; + } + cpu_dai->active++; + codec_dai->active++; + rtd->codec->active++; + mutex_unlock(&rtd->pcm_mutex); + return 0; + +config_err: + if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + rtd->dai_link->ops->shutdown(substream); + +machine_err: + if (codec_dai->driver->ops->shutdown) + codec_dai->driver->ops->shutdown(substream, codec_dai); + +codec_dai_err: + if (platform->driver->ops && platform->driver->ops->close) + platform->driver->ops->close(substream); + +platform_err: + if (cpu_dai->driver->ops->shutdown) + cpu_dai->driver->ops->shutdown(substream, cpu_dai); +out: + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + +/* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +static void close_delayed_work(struct work_struct *work) +{ + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + pr_debug("pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + codec_dai->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (codec_dai->pop_wait == 1) { + codec_dai->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +} + +/* + * Called by ALSA when a PCM substream is closed. Private data can be + * freed here. The cpu DAI, codec DAI, machine and platform are also + * shutdown. + */ +static int soc_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active--; + codec_dai->playback_active--; + } else { + cpu_dai->capture_active--; + codec_dai->capture_active--; + } + + cpu_dai->active--; + codec_dai->active--; + codec->active--; + + /* Muting the DAC suppresses artifacts caused during digital + * shutdown, for example from stopping clocks. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_digital_mute(codec_dai, 1); + + if (cpu_dai->driver->ops->shutdown) + cpu_dai->driver->ops->shutdown(substream, cpu_dai); + + if (codec_dai->driver->ops->shutdown) + codec_dai->driver->ops->shutdown(substream, codec_dai); + + if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + rtd->dai_link->ops->shutdown(substream); + + if (platform->driver->ops && platform->driver->ops->close) + platform->driver->ops->close(substream); + cpu_dai->runtime = NULL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* start delayed pop wq here for playback streams */ + codec_dai->pop_wait = 1; + schedule_delayed_work(&rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } else { + /* capture streams can be powered down now */ + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); + return 0; +} + +/* + * Called by ALSA when the PCM substream is prepared, can set format, sample + * rate, etc. This function is non atomic and can be called multiple times, + * it can refer to the runtime info. + */ +static int soc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { + ret = rtd->dai_link->ops->prepare(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: machine prepare error\n"); + goto out; + } + } + + if (platform->driver->ops && platform->driver->ops->prepare) { + ret = platform->driver->ops->prepare(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: platform prepare error\n"); + goto out; + } + } + + if (codec_dai->driver->ops->prepare) { + ret = codec_dai->driver->ops->prepare(substream, codec_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: codec DAI prepare error\n"); + goto out; + } + } + + if (cpu_dai->driver->ops->prepare) { + ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: cpu DAI prepare error\n"); + goto out; + } + } + + /* cancel any delayed stream shutdown that is pending */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + codec_dai->pop_wait) { + codec_dai->pop_wait = 0; + cancel_delayed_work(&rtd->delayed_work); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_START); + else + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + + snd_soc_dai_digital_mute(codec_dai, 0); + +out: + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + +/* + * Called by ALSA when the hardware params are set by application. This + * function can also be called multiple times and can allocate buffers + * (using snd_pcm_lib_* ). It's non-atomic. + */ +static int soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { + ret = rtd->dai_link->ops->hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: machine hw_params failed\n"); + goto out; + } + } + + if (codec_dai->driver->ops->hw_params) { + ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: can't set codec %s hw params\n", + codec_dai->name); + goto codec_err; + } + } + + if (cpu_dai->driver->ops->hw_params) { + ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); + if (ret < 0) { + printk(KERN_ERR "asoc: interface %s hw params failed\n", + cpu_dai->name); + goto interface_err; + } + } + + if (platform->driver->ops && platform->driver->ops->hw_params) { + ret = platform->driver->ops->hw_params(substream, params); + if (ret < 0) { + printk(KERN_ERR "asoc: platform %s hw params failed\n", + platform->name); + goto platform_err; + } + } + + rtd->rate = params_rate(params); + +out: + mutex_unlock(&rtd->pcm_mutex); + return ret; + +platform_err: + if (cpu_dai->driver->ops->hw_free) + cpu_dai->driver->ops->hw_free(substream, cpu_dai); + +interface_err: + if (codec_dai->driver->ops->hw_free) + codec_dai->driver->ops->hw_free(substream, codec_dai); + +codec_err: + if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + rtd->dai_link->ops->hw_free(substream); + + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + +/* + * Frees resources allocated by hw_params, can be called multiple times + */ +static int soc_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + /* apply codec digital mute */ + if (!codec->active) + snd_soc_dai_digital_mute(codec_dai, 1); + + /* free any machine hw params */ + if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + rtd->dai_link->ops->hw_free(substream); + + /* free any DMA resources */ + if (platform->driver->ops && platform->driver->ops->hw_free) + platform->driver->ops->hw_free(substream); + + /* now free hw params for the DAIs */ + if (codec_dai->driver->ops->hw_free) + codec_dai->driver->ops->hw_free(substream, codec_dai); + + if (cpu_dai->driver->ops->hw_free) + cpu_dai->driver->ops->hw_free(substream, cpu_dai); + + mutex_unlock(&rtd->pcm_mutex); + return 0; +} + +static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + if (codec_dai->driver->ops->trigger) { + ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); + if (ret < 0) + return ret; + } + + if (platform->driver->ops && platform->driver->ops->trigger) { + ret = platform->driver->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (cpu_dai->driver->ops->trigger) { + ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); + if (ret < 0) + return ret; + } + return 0; +} + +/* + * soc level wrapper for pointer callback + * If cpu_dai, codec_dai, platform driver has the delay callback, than + * the runtime->delay will be updated accordingly. + */ +static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t offset = 0; + snd_pcm_sframes_t delay = 0; + + if (platform->driver->ops && platform->driver->ops->pointer) + offset = platform->driver->ops->pointer(substream); + + if (cpu_dai->driver->ops->delay) + delay += cpu_dai->driver->ops->delay(substream, cpu_dai); + + if (codec_dai->driver->ops->delay) + delay += codec_dai->driver->ops->delay(substream, codec_dai); + + if (platform->driver->delay) + delay += platform->driver->delay(substream, codec_dai); + + runtime->delay = delay; + + return offset; +} + +/* ASoC PCM operations */ +static struct snd_pcm_ops soc_pcm_ops = { + .open = soc_pcm_open, + .close = soc_pcm_close, + .hw_params = soc_pcm_hw_params, + .hw_free = soc_pcm_hw_free, + .prepare = soc_pcm_prepare, + .trigger = soc_pcm_trigger, + .pointer = soc_pcm_pointer, +}; + +/* create a new pcm */ +int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_pcm *pcm; + char new_name[64]; + int ret = 0, playback = 0, capture = 0; + + /* check client and interface hw capabilities */ + snprintf(new_name, sizeof(new_name), "%s %s-%d", + rtd->dai_link->stream_name, codec_dai->name, num); + + if (codec_dai->driver->playback.channels_min) + playback = 1; + if (codec_dai->driver->capture.channels_min) + capture = 1; + + dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); + ret = snd_pcm_new(rtd->card->snd_card, new_name, + num, playback, capture, &pcm); + if (ret < 0) { + printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); + return ret; + } + + /* DAPM dai link stream work */ + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + + rtd->pcm = pcm; + pcm->private_data = rtd; + if (platform->driver->ops) { + soc_pcm_ops.mmap = platform->driver->ops->mmap; + soc_pcm_ops.pointer = platform->driver->ops->pointer; + soc_pcm_ops.ioctl = platform->driver->ops->ioctl; + soc_pcm_ops.copy = platform->driver->ops->copy; + soc_pcm_ops.silence = platform->driver->ops->silence; + soc_pcm_ops.ack = platform->driver->ops->ack; + soc_pcm_ops.page = platform->driver->ops->page; + } + + if (playback) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); + + if (capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); + + if (platform->driver->pcm_new) { + ret = platform->driver->pcm_new(rtd); + if (ret < 0) { + pr_err("asoc: platform pcm constructor failed\n"); + return ret; + } + } + + pcm->private_free = platform->driver->pcm_free; + printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, + cpu_dai->name); + return ret; +} diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 035d39a4beb4..c6af1fd707f5 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -12,6 +12,15 @@ config SND_SOC_TEGRA_I2S Tegra I2S interface. You will also need to select the individual machine drivers to support below. +config SND_SOC_TEGRA_SPDIF + tristate + depends on SND_SOC_TEGRA + default m + help + Say Y or M if you want to add support for the SPDIF interface. + You will also need to select the individual machine drivers to support + below. + config MACH_HAS_SND_SOC_TEGRA_WM8903 bool help diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index fa6574d92a31..4d943b3fe150 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -2,12 +2,14 @@ snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-i2s-objs := tegra_i2s.o +snd-soc-tegra-spdif-objs := tegra_spdif.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o +obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 95f03c10b4f7..f36b9969cfec 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -354,7 +354,6 @@ struct snd_soc_dai_driver tegra_i2s_dai[] = { static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) { struct tegra_i2s * i2s; - char clk_name[12]; /* tegra-i2s.0 */ struct resource *mem, *memregion, *dmareq; int ret; @@ -389,8 +388,7 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, i2s); - snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id); - i2s->clk_i2s = clk_get_sys(clk_name, NULL); + i2s->clk_i2s = clk_get(&pdev->dev, NULL); if (IS_ERR(i2s->clk_i2s)) { dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); ret = PTR_ERR(i2s->clk_i2s); diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 3c271f953582..ff86e5e3db68 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -322,9 +322,11 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) static u64 tegra_dma_mask = DMA_BIT_MASK(32); -static int tegra_pcm_new(struct snd_card *card, - struct snd_soc_dai *dai, struct snd_pcm *pcm) +static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c new file mode 100644 index 000000000000..abe606b0a29e --- /dev/null +++ b/sound/soc/tegra/tegra_spdif.c @@ -0,0 +1,371 @@ +/* + * tegra_spdif.c - Tegra SPDIF driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2011 - NVIDIA, Inc. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <mach/iomap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_spdif.h" + +#define DRV_NAME "tegra-spdif" + +static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg, + u32 val) +{ + __raw_writel(val, spdif->regs + reg); +} + +static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg) +{ + return __raw_readl(spdif->regs + reg); +} + +#ifdef CONFIG_DEBUG_FS +static int tegra_spdif_show(struct seq_file *s, void *unused) +{ +#define REG(r) { r, #r } + static const struct { + int offset; + const char *name; + } regs[] = { + REG(TEGRA_SPDIF_CTRL), + REG(TEGRA_SPDIF_STATUS), + REG(TEGRA_SPDIF_STROBE_CTRL), + REG(TEGRA_SPDIF_DATA_FIFO_CSR), + REG(TEGRA_SPDIF_CH_STA_RX_A), + REG(TEGRA_SPDIF_CH_STA_RX_B), + REG(TEGRA_SPDIF_CH_STA_RX_C), + REG(TEGRA_SPDIF_CH_STA_RX_D), + REG(TEGRA_SPDIF_CH_STA_RX_E), + REG(TEGRA_SPDIF_CH_STA_RX_F), + REG(TEGRA_SPDIF_CH_STA_TX_A), + REG(TEGRA_SPDIF_CH_STA_TX_B), + REG(TEGRA_SPDIF_CH_STA_TX_C), + REG(TEGRA_SPDIF_CH_STA_TX_D), + REG(TEGRA_SPDIF_CH_STA_TX_E), + REG(TEGRA_SPDIF_CH_STA_TX_F), + }; +#undef REG + + struct tegra_spdif *spdif = s->private; + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + u32 val = tegra_spdif_read(spdif, regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + + return 0; +} + +static int tegra_spdif_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_spdif_show, inode->i_private); +} + +static const struct file_operations tegra_spdif_debug_fops = { + .open = tegra_spdif_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra_spdif_debug_add(struct tegra_spdif *spdif) +{ + spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, spdif, + &tegra_spdif_debug_fops); +} + +static void tegra_spdif_debug_remove(struct tegra_spdif *spdif) +{ + if (spdif->debug) + debugfs_remove(spdif->debug); +} +#else +static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif) +{ +} + +static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif) +{ +} +#endif + +static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + int ret, srate, spdifclock; + + spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK; + spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK; + spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + switch (params_rate(params)) { + case 32000: + spdifclock = 4096000; + break; + case 44100: + spdifclock = 5644800; + break; + case 48000: + spdifclock = 6144000; + break; + case 88200: + spdifclock = 11289600; + break; + case 96000: + spdifclock = 12288000; + break; + case 176400: + spdifclock = 22579200; + break; + case 192000: + spdifclock = 24576000; + break; + default: + return -EINVAL; + } + + ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); + if (ret) { + dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + return ret; + } + + return 0; +} + +static void tegra_spdif_start_playback(struct tegra_spdif *spdif) +{ + spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN; + tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); +} + +static void tegra_spdif_stop_playback(struct tegra_spdif *spdif) +{ + spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN; + tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); +} + +static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (!spdif->clk_refs) + clk_enable(spdif->clk_spdif_out); + spdif->clk_refs++; + tegra_spdif_start_playback(spdif); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tegra_spdif_stop_playback(spdif); + spdif->clk_refs--; + if (!spdif->clk_refs) + clk_disable(spdif->clk_spdif_out); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_spdif_probe(struct snd_soc_dai *dai) +{ + struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = NULL; + dai->playback_dma_data = &spdif->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops tegra_spdif_dai_ops = { + .hw_params = tegra_spdif_hw_params, + .trigger = tegra_spdif_trigger, +}; + +struct snd_soc_dai_driver tegra_spdif_dai = { + .name = DRV_NAME, + .probe = tegra_spdif_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra_spdif_dai_ops, +}; + +static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) +{ + struct tegra_spdif *spdif; + struct resource *mem, *memregion, *dmareq; + int ret; + + spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL); + if (!spdif) { + dev_err(&pdev->dev, "Can't allocate tegra_spdif\n"); + ret = -ENOMEM; + goto exit; + } + dev_set_drvdata(&pdev->dev, spdif); + + spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out"); + if (IS_ERR(spdif->clk_spdif_out)) { + pr_err("Can't retrieve spdif clock\n"); + ret = PTR_ERR(spdif->clk_spdif_out); + goto err_free; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmareq) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = request_mem_region(mem->start, resource_size(mem), + DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + spdif->regs = ioremap(mem->start, resource_size(mem)); + if (!spdif->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release; + } + + spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT; + spdif->playback_dma_data.wrap = 4; + spdif->playback_dma_data.width = 32; + spdif->playback_dma_data.req_sel = dmareq->start; + + ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_unmap; + } + + tegra_spdif_debug_add(spdif); + + return 0; + +err_unmap: + iounmap(spdif->regs); +err_release: + release_mem_region(mem->start, resource_size(mem)); +err_clk_put: + clk_put(spdif->clk_spdif_out); +err_free: + kfree(spdif); +exit: + return ret; +} + +static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev) +{ + struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev); + struct resource *res; + + snd_soc_unregister_dai(&pdev->dev); + + tegra_spdif_debug_remove(spdif); + + iounmap(spdif->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_put(spdif->clk_spdif_out); + + kfree(spdif); + + return 0; +} + +static struct platform_driver tegra_spdif_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_spdif_platform_probe, + .remove = __devexit_p(tegra_spdif_platform_remove), +}; + +static int __init snd_tegra_spdif_init(void) +{ + return platform_driver_register(&tegra_spdif_driver); +} +module_init(snd_tegra_spdif_init); + +static void __exit snd_tegra_spdif_exit(void) +{ + platform_driver_unregister(&tegra_spdif_driver); +} +module_exit(snd_tegra_spdif_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra SPDIF ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h new file mode 100644 index 000000000000..2e03db430279 --- /dev/null +++ b/sound/soc/tegra/tegra_spdif.h @@ -0,0 +1,473 @@ +/* + * tegra_spdif.h - Definitions for Tegra SPDIF driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2011 - NVIDIA, Inc. + * + * Based on code copyright/by: + * Copyright (c) 2008-2009, NVIDIA Corporation + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA_SPDIF_H__ +#define __TEGRA_SPDIF_H__ + +#include "tegra_pcm.h" + +/* Offsets from TEGRA_SPDIF_BASE */ + +#define TEGRA_SPDIF_CTRL 0x0 +#define TEGRA_SPDIF_STATUS 0x4 +#define TEGRA_SPDIF_STROBE_CTRL 0x8 +#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C +#define TEGRA_SPDIF_DATA_OUT 0x40 +#define TEGRA_SPDIF_DATA_IN 0x80 +#define TEGRA_SPDIF_CH_STA_RX_A 0x100 +#define TEGRA_SPDIF_CH_STA_RX_B 0x104 +#define TEGRA_SPDIF_CH_STA_RX_C 0x108 +#define TEGRA_SPDIF_CH_STA_RX_D 0x10C +#define TEGRA_SPDIF_CH_STA_RX_E 0x110 +#define TEGRA_SPDIF_CH_STA_RX_F 0x114 +#define TEGRA_SPDIF_CH_STA_TX_A 0x140 +#define TEGRA_SPDIF_CH_STA_TX_B 0x144 +#define TEGRA_SPDIF_CH_STA_TX_C 0x148 +#define TEGRA_SPDIF_CH_STA_TX_D 0x14C +#define TEGRA_SPDIF_CH_STA_TX_E 0x150 +#define TEGRA_SPDIF_CH_STA_TX_F 0x154 +#define TEGRA_SPDIF_USR_STA_RX_A 0x180 +#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0 + +/* Fields in TEGRA_SPDIF_CTRL */ + +/* Start capturing from 0=right, 1=left channel */ +#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30) + +/* SPDIF receiver(RX) enable */ +#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29) + +/* SPDIF Transmitter(TX) enable */ +#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28) + +/* Transmit Channel status */ +#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27) + +/* Transmit user Data */ +#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26) + +/* Interrupt on transmit error */ +#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25) + +/* Interrupt on receive error */ +#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24) + +/* Interrupt on invalid preamble */ +#define TEGRA_SPDIF_CTRL_IE_P (1 << 23) + +/* Interrupt on "B" preamble */ +#define TEGRA_SPDIF_CTRL_IE_B (1 << 22) + +/* Interrupt when block of channel status received */ +#define TEGRA_SPDIF_CTRL_IE_C (1 << 21) + +/* Interrupt when a valid information unit (IU) is received */ +#define TEGRA_SPDIF_CTRL_IE_U (1 << 20) + +/* Interrupt when RX user FIFO attention level is reached */ +#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19) + +/* Interrupt when TX user FIFO attention level is reached */ +#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18) + +/* Interrupt when RX data FIFO attention level is reached */ +#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17) + +/* Interrupt when TX data FIFO attention level is reached */ +#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16) + +/* Loopback test mode enable */ +#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15) + +/* + * Pack data mode: + * 0 = Single data (16 bit needs to be padded to match the + * interface data bit size). + * 1 = Packeted left/right channel data into a single word. + */ +#define TEGRA_SPDIF_CTRL_PACK (1 << 14) + +/* + * 00 = 16bit data + * 01 = 20bit data + * 10 = 24bit data + * 11 = raw data + */ +#define TEGRA_SPDIF_BIT_MODE_16BIT 0 +#define TEGRA_SPDIF_BIT_MODE_20BIT 1 +#define TEGRA_SPDIF_BIT_MODE_24BIT 2 +#define TEGRA_SPDIF_BIT_MODE_RAW 3 + +#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12 +#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) + +/* Fields in TEGRA_SPDIF_STATUS */ + +/* + * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must + * write a 1 to the corresponding bit location to clear the status. + */ + +/* + * Receiver(RX) shifter is busy receiving data. + * This bit is asserted when the receiver first locked onto the + * preamble of the data stream after RX_EN is asserted. This bit is + * deasserted when either, + * (a) the end of a frame is reached after RX_EN is deeasserted, or + * (b) the SPDIF data stream becomes inactive. + */ +#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29) + +/* + * Transmitter(TX) shifter is busy transmitting data. + * This bit is asserted when TX_EN is asserted. + * This bit is deasserted when the end of a frame is reached after + * TX_EN is deasserted. + */ +#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28) + +/* + * TX is busy shifting out channel status. + * This bit is asserted when both TX_EN and TC_EN are asserted and + * data from CH_STA_TX_A register is loaded into the internal shifter. + * This bit is deasserted when either, + * (a) the end of a frame is reached after TX_EN is deasserted, or + * (b) CH_STA_TX_F register is loaded into the internal shifter. + */ +#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27) + +/* + * TX User data FIFO busy. + * This bit is asserted when TX_EN and TXU_EN are asserted and + * there's data in the TX user FIFO. This bit is deassert when either, + * (a) the end of a frame is reached after TX_EN is deasserted, or + * (b) there's no data left in the TX user FIFO. + */ +#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26) + +/* TX FIFO Underrun error status */ +#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25) + +/* RX FIFO Overrun error status */ +#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24) + +/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */ +#define TEGRA_SPDIF_STATUS_IS_P (1 << 23) + +/* B-preamble detection status: 0=not detected, 1=B-preamble detected */ +#define TEGRA_SPDIF_STATUS_IS_B (1 << 22) + +/* + * RX channel block data receive status: + * 0=entire block not recieved yet. + * 1=received entire block of channel status, + */ +#define TEGRA_SPDIF_STATUS_IS_C (1 << 21) + +/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */ +#define TEGRA_SPDIF_STATUS_IS_U (1 << 20) + +/* + * RX User FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19) + +/* + * TX User FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18) + +/* + * RX Data FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17) + +/* + * TX Data FIFO Status: + * 1=attention level reached, 0=attention level not reached. + */ +#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16) + +/* Fields in TEGRA_SPDIF_STROBE_CTRL */ + +/* + * Indicates the approximate number of detected SPDIFIN clocks within a + * bi-phase period. + */ +#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16 +#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT) + +/* Data strobe mode: 0=Auto-locked 1=Manual locked */ +#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15) + +/* + * Manual data strobe time within the bi-phase clock period (in terms of + * the number of over-sampling clocks). + */ +#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8 +#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT) + +/* + * Manual SPDIFIN bi-phase clock period (in terms of the number of + * over-sampling clocks). + */ +#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0 +#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT) + +/* Fields in SPDIF_DATA_FIFO_CSR */ + +/* Clear Receiver User FIFO (RX USR.FIFO) */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31) + +#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0 +#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1 +#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2 +#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3 + +/* RU FIFO attention level */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29 +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \ + (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) + +/* Number of RX USR.FIFO levels with valid data. */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24 +#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT) + +/* Clear Transmitter User FIFO (TX USR.FIFO) */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23) + +/* TU FIFO attention level */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21 +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \ + (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) + +/* Number of TX USR.FIFO levels that could be filled. */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16 +#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT) + +/* Clear Receiver Data FIFO (RX DATA.FIFO) */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15) + +#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0 +#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1 +#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2 +#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3 + +/* RU FIFO attention level */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13 +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \ + (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) + +/* Number of RX DATA.FIFO levels with valid data. */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8 +#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT) + +/* Clear Transmitter Data FIFO (TX DATA.FIFO) */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7) + +/* TU FIFO attention level */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5 +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \ + (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \ + (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) + +/* Number of TX DATA.FIFO levels that could be filled. */ +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0 +#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT) + +/* Fields in TEGRA_SPDIF_DATA_OUT */ + +/* + * This register has 5 different formats: + * 16-bit (BIT_MODE=00, PACK=0) + * 20-bit (BIT_MODE=01, PACK=0) + * 24-bit (BIT_MODE=10, PACK=0) + * raw (BIT_MODE=11, PACK=0) + * 16-bit packed (BIT_MODE=00, PACK=1) + */ + +#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0 +#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0 +#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0 +#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31) +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30) +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29) +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28) + +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8 +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4 +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT) + +#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT) + +/* Fields in TEGRA_SPDIF_DATA_IN */ + +/* + * This register has 5 different formats: + * 16-bit (BIT_MODE=00, PACK=0) + * 20-bit (BIT_MODE=01, PACK=0) + * 24-bit (BIT_MODE=10, PACK=0) + * raw (BIT_MODE=11, PACK=0) + * 16-bit packed (BIT_MODE=00, PACK=1) + * + * Bits 31:24 are common to all modes except 16-bit packed + */ + +#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31) +#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30) +#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29) +#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28) + +#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24 +#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0 +#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0 +#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0 +#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8 +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4 +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT) + +#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT) + +/* Fields in TEGRA_SPDIF_CH_STA_RX_A */ +/* Fields in TEGRA_SPDIF_CH_STA_RX_B */ +/* Fields in TEGRA_SPDIF_CH_STA_RX_C */ +/* Fields in TEGRA_SPDIF_CH_STA_RX_D */ +/* Fields in TEGRA_SPDIF_CH_STA_RX_E */ +/* Fields in TEGRA_SPDIF_CH_STA_RX_F */ + +/* + * The 6-word receive channel data page buffer holds a block (192 frames) of + * channel status information. The order of receive is from LSB to MSB + * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A. + */ + +/* Fields in TEGRA_SPDIF_CH_STA_TX_A */ +/* Fields in TEGRA_SPDIF_CH_STA_TX_B */ +/* Fields in TEGRA_SPDIF_CH_STA_TX_C */ +/* Fields in TEGRA_SPDIF_CH_STA_TX_D */ +/* Fields in TEGRA_SPDIF_CH_STA_TX_E */ +/* Fields in TEGRA_SPDIF_CH_STA_TX_F */ + +/* + * The 6-word transmit channel data page buffer holds a block (192 frames) of + * channel status information. The order of transmission is from LSB to MSB + * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A. + */ + +/* Fields in TEGRA_SPDIF_USR_STA_RX_A */ + +/* + * This 4-word deep FIFO receives user FIFO field information. The order of + * receive is from LSB to MSB bit. + */ + +/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */ + +/* + * This 4-word deep FIFO transmits user FIFO field information. The order of + * transmission is from LSB to MSB bit. + */ + +struct tegra_spdif { + struct clk *clk_spdif_out; + int clk_refs; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + void __iomem *regs; + struct dentry *debug; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 0d6738a8b29a..a42e9ac30f28 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -267,7 +267,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) } machine->gpio_requested |= GPIO_HP_MUTE; - gpio_direction_output(pdata->gpio_hp_mute, 0); + gpio_direction_output(pdata->gpio_hp_mute, 1); } if (gpio_is_valid(pdata->gpio_int_mic_en)) { diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index f4aa4e03c888..34aa972669ed 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -288,9 +288,10 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; struct platform_device *pdev = to_platform_device(dai->platform->dev); struct txx9aclc_soc_device *dev; struct resource *r; diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 73f9cbacc077..1b839a0f3653 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -69,7 +69,7 @@ #include <linux/of.h> #include <linux/of_device.h> -#include <asm/atomic.h> +#include <linux/atomic.h> MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets"); MODULE_DESCRIPTION("Sun DBRI"); diff --git a/sound/usb/card.c b/sound/usb/card.c index 220c6167dd86..781d9e61adfb 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -433,9 +433,10 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -static void *snd_usb_audio_probe(struct usb_device *dev, - struct usb_interface *intf, - const struct usb_device_id *usb_id) +static struct snd_usb_audio * +snd_usb_audio_probe(struct usb_device *dev, + struct usb_interface *intf, + const struct usb_device_id *usb_id) { const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; int i, err; @@ -540,16 +541,15 @@ static void *snd_usb_audio_probe(struct usb_device *dev, * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) +static void snd_usb_audio_disconnect(struct usb_device *dev, + struct snd_usb_audio *chip) { - struct snd_usb_audio *chip; struct snd_card *card; struct list_head *p; - if (ptr == (void *)-1L) + if (chip == (void *)-1L) return; - chip = ptr; card = chip->card; mutex_lock(®ister_mutex); mutex_lock(&chip->shutdown_mutex); @@ -585,7 +585,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id) { - void *chip; + struct snd_usb_audio *chip; chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); if (chip) { usb_set_intfdata(intf, chip); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index b0ef9f501896..7c0d21ecd821 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -408,6 +408,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) /* doesn't set the sample rate attribute, but supports it */ fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; break; + case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */ + case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is an older model 77d:223) */ diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index fb5d68fa7ff4..67bec7612442 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -645,7 +645,7 @@ static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1500000 / ua->packets_per_second, - 8192000); + UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 0b2ae8e1c02d..dba0b7f11c54 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1677,6 +1677,36 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE(0x0582, 0x011e), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "BOSS", */ + /* .product_name = "BR-800", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, /* Guillemot devices */ { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 090e1930dfdc..77762c99afbe 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -369,6 +369,30 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) return 0; } +static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev) +{ + int err; + + if (dev->actconfig->desc.bConfigurationValue == 1) { + snd_printk(KERN_INFO "usb-audio: " + "Fast Track Pro switching to config #2\n"); + /* This function has to be available by the usb core module. + * if it is not avialable the boot quirk has to be left out + * and the configuration has to be set by udev or hotplug + * rules + */ + err = usb_driver_set_configuration(dev, 2); + if (err < 0) { + snd_printdd("error usb_driver_set_configuration: %d\n", + err); + return -ENODEV; + } + } else + snd_printk(KERN_INFO "usb-audio: Fast Track Pro config OK\n"); + + return 0; +} + /* * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely * documented in the device's data sheet. @@ -471,16 +495,49 @@ static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev) /* * Setup quirks */ -#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */ -#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */ -#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */ -#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */ -#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */ -#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */ -#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */ -#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */ -#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */ -#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */ +#define MAUDIO_SET 0x01 /* parse device_setup */ +#define MAUDIO_SET_COMPATIBLE 0x80 /* use only "win-compatible" interfaces */ +#define MAUDIO_SET_DTS 0x02 /* enable DTS Digital Output */ +#define MAUDIO_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */ +#define MAUDIO_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */ +#define MAUDIO_SET_DI 0x10 /* enable Digital Input */ +#define MAUDIO_SET_MASK 0x1f /* bit mask for setup value */ +#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48KHz+Digital Input */ +#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48KHz+No Digital Input */ +#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48KHz+Digital Input */ +#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48KHz+No Digital Input */ + +static int quattro_skip_setting_quirk(struct snd_usb_audio *chip, + int iface, int altno) +{ + /* Reset ALL ifaces to 0 altsetting. + * Call it for every possible altsetting of every interface. + */ + usb_set_interface(chip->dev, iface, 0); + if (chip->setup & MAUDIO_SET) { + if (chip->setup & MAUDIO_SET_COMPATIBLE) { + if (iface != 1 && iface != 2) + return 1; /* skip all interfaces but 1 and 2 */ + } else { + unsigned int mask; + if (iface == 1 || iface == 2) + return 1; /* skip interfaces 1 and 2 */ + if ((chip->setup & MAUDIO_SET_96K) && altno != 1) + return 1; /* skip this altsetting */ + mask = chip->setup & MAUDIO_SET_MASK; + if (mask == MAUDIO_SET_24B_48K_DI && altno != 2) + return 1; /* skip this altsetting */ + if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3) + return 1; /* skip this altsetting */ + if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 4) + return 1; /* skip this altsetting */ + } + } + snd_printdd(KERN_INFO + "using altsetting %d for interface %d config %d\n", + altno, iface, chip->setup); + return 0; /* keep this altsetting */ +} static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, int iface, @@ -491,30 +548,65 @@ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, */ usb_set_interface(chip->dev, iface, 0); - if (chip->setup & AUDIOPHILE_SET) { - if ((chip->setup & AUDIOPHILE_SET_DTS) - && altno != 6) + if (chip->setup & MAUDIO_SET) { + unsigned int mask; + if ((chip->setup & MAUDIO_SET_DTS) && altno != 6) return 1; /* skip this altsetting */ - if ((chip->setup & AUDIOPHILE_SET_96K) - && altno != 1) + if ((chip->setup & MAUDIO_SET_96K) && altno != 1) return 1; /* skip this altsetting */ - if ((chip->setup & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_24B_48K_DI && altno != 2) + mask = chip->setup & MAUDIO_SET_MASK; + if (mask == MAUDIO_SET_24B_48K_DI && altno != 2) return 1; /* skip this altsetting */ - if ((chip->setup & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3) + if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3) return 1; /* skip this altsetting */ - if ((chip->setup & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_16B_48K_DI && altno != 4) + if (mask == MAUDIO_SET_16B_48K_DI && altno != 4) return 1; /* skip this altsetting */ - if ((chip->setup & AUDIOPHILE_SET_MASK) == - AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5) + if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 5) return 1; /* skip this altsetting */ } return 0; /* keep this altsetting */ } + +static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip, + int iface, int altno) +{ + /* Reset ALL ifaces to 0 altsetting. + * Call it for every possible altsetting of every interface. + */ + usb_set_interface(chip->dev, iface, 0); + + /* possible configuration where both inputs and only one output is + *used is not supported by the current setup + */ + if (chip->setup & (MAUDIO_SET | MAUDIO_SET_24B)) { + if (chip->setup & MAUDIO_SET_96K) { + if (altno != 3 && altno != 6) + return 1; + } else if (chip->setup & MAUDIO_SET_DI) { + if (iface == 4) + return 1; /* no analog input */ + if (altno != 2 && altno != 5) + return 1; /* enable only altsets 2 and 5 */ + } else { + if (iface == 5) + return 1; /* disable digialt input */ + if (altno != 2 && altno != 5) + return 1; /* enalbe only altsets 2 and 5 */ + } + } else { + /* keep only 16-Bit mode */ + if (altno != 1) + return 1; + } + + snd_printdd(KERN_INFO + "using altsetting %d for interface %d config %d\n", + altno, iface, chip->setup); + return 0; /* keep this altsetting */ +} + int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int iface, int altno) @@ -522,6 +614,12 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, /* audiophile usb: skip altsets incompatible with device_setup */ if (chip->usb_id == USB_ID(0x0763, 0x2003)) return audiophile_skip_setting_quirk(chip, iface, altno); + /* quattro usb: skip altsets incompatible with device_setup */ + if (chip->usb_id == USB_ID(0x0763, 0x2001)) + return quattro_skip_setting_quirk(chip, iface, altno); + /* fasttrackpro usb: skip altsets incompatible with device_setup */ + if (chip->usb_id == USB_ID(0x0763, 0x2012)) + return fasttrackpro_skip_setting_quirk(chip, iface, altno); return 0; } @@ -560,6 +658,8 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */ case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */ return snd_usb_nativeinstruments_boot_quirk(dev); + case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ + return snd_usb_fasttrackpro_boot_quirk(dev); } return 0; @@ -570,15 +670,24 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, */ int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp) { + /* it depends on altsetting wether the device is big-endian or not */ switch (chip->usb_id) { case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */ - if (fp->endpoint & USB_DIR_IN) + if (fp->altsetting == 2 || fp->altsetting == 3 || + fp->altsetting == 5 || fp->altsetting == 6) return 1; break; case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ if (chip->setup == 0x00 || - fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3) + fp->altsetting == 1 || fp->altsetting == 2 || + fp->altsetting == 3) + return 1; + break; + case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro */ + if (fp->altsetting == 2 || fp->altsetting == 3 || + fp->altsetting == 5 || fp->altsetting == 6) return 1; + break; } return 0; } |