diff options
-rw-r--r-- | Documentation/sound/alsa/HD-Audio-Models.txt | 2 | ||||
-rw-r--r-- | Documentation/sound/alsa/HD-Audio.txt | 27 | ||||
-rw-r--r-- | include/sound/pcm.h | 10 | ||||
-rw-r--r-- | include/sound/pcm_oss.h | 2 | ||||
-rw-r--r-- | include/sound/version.h | 2 | ||||
-rw-r--r-- | sound/core/oss/pcm_oss.c | 32 | ||||
-rw-r--r-- | sound/core/pcm.c | 4 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 418 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 8 | ||||
-rw-r--r-- | sound/pci/ac97/ac97_patch.c | 17 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 77 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 5 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 60 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 51 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 14 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 31 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 104 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 14 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 11 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 474 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 755 | ||||
-rw-r--r-- | sound/pci/hda/patch_si3054.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 183 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 274 |
25 files changed, 1732 insertions, 847 deletions
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index e72cee9e2a7..8f06f20096f 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -279,7 +279,9 @@ Conexant 5051 laptop Basic Laptop config (default) hp HP Spartan laptop hp-dv6736 HP dv6736 + hp-f700 HP Compaq Presario F700 lenovo-x200 Lenovo X200 laptop + toshiba Toshiba Satellite M300 Conexant 5066 ============= diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 6325bec06a7..f4dd3bf99d1 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -452,6 +452,33 @@ Similarly, the lines after `[verb]` are parsed as `init_verbs` sysfs entries, and the lines after `[hint]` are parsed as `hints` sysfs entries, respectively. +Another example to override the codec vendor id from 0x12345678 to +0xdeadbeef is like below: +------------------------------------------------------------------------ + [codec] + 0x12345678 0xabcd1234 2 + + [vendor_id] + 0xdeadbeef +------------------------------------------------------------------------ + +In the similar way, you can override the codec subsystem_id via +`[subsystem_id]`, the revision id via `[revision_id]` line. +Also, the codec chip name can be rewritten via `[chip_name]` line. +------------------------------------------------------------------------ + [codec] + 0x12345678 0xabcd1234 2 + + [subsystem_id] + 0xffff1111 + + [revision_id] + 0x10 + + [chip_name] + My-own NEWS-0002 +------------------------------------------------------------------------ + The hd-audio driver reads the file via request_firmware(). Thus, a patch file has to be located on the appropriate firmware path, typically, /lib/firmware. For example, when you pass the option diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c83a4a79f16..e26fb3c5803 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -262,6 +262,8 @@ struct snd_pcm_hw_constraint_list { unsigned int mask; }; +struct snd_pcm_hwptr_log; + struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; @@ -269,7 +271,6 @@ struct snd_pcm_runtime { int overrange; snd_pcm_uframes_t avail_max; snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ - snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ @@ -310,6 +311,7 @@ struct snd_pcm_runtime { struct snd_pcm_mmap_control *control; /* -- locking / scheduling -- */ + unsigned int nowake: 1; /* no wakeup (data-copy in progress) */ wait_queue_head_t sleep; struct fasync_struct *fasync; @@ -340,6 +342,10 @@ struct snd_pcm_runtime { /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif + +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + struct snd_pcm_hwptr_log *hwptr_log; +#endif }; struct snd_pcm_group { /* keep linked substreams */ @@ -834,6 +840,8 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream); int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream); int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime); int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream); int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream); diff --git a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h index cc4e226f35f..760c969d885 100644 --- a/include/sound/pcm_oss.h +++ b/include/sound/pcm_oss.h @@ -61,7 +61,7 @@ struct snd_pcm_oss_runtime { struct snd_pcm_plugin *plugin_first; struct snd_pcm_plugin *plugin_last; #endif - unsigned int prev_hw_ptr_interrupt; + unsigned int prev_hw_ptr_period; }; struct snd_pcm_oss_file { diff --git a/include/sound/version.h b/include/sound/version.h index 22939142dd2..7fed23442db 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.21" +#define CONFIG_SND_VERSION "1.0.22.1" #define CONFIG_SND_DATE "" diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d9c96353121..255ad910077 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -632,6 +632,13 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); } +static inline +snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) +{ + snd_pcm_uframes_t ptr = runtime->status->hw_ptr; + return ptr - (ptr % runtime->period_size); +} + /* define extended formats in the recent OSS versions (if any) */ /* linear formats */ #define AFMT_S32_LE 0x00001000 @@ -1102,7 +1109,7 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) return err; } runtime->oss.prepare = 0; - runtime->oss.prev_hw_ptr_interrupt = 0; + runtime->oss.prev_hw_ptr_period = 0; runtime->oss.period_ptr = 0; runtime->oss.buffer_used = 0; @@ -1950,7 +1957,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file) return result; } -static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr) +static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hw_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t appl_ptr; @@ -1986,7 +1994,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr if (runtime->oss.trigger) goto _skip1; if (atomic_read(&psubstream->mmap_count)) - snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); + snd_pcm_oss_simulate_fill(psubstream, + get_hw_ptr_period(runtime)); runtime->oss.trigger = 1; runtime->start_threshold = 1; cmd = SNDRV_PCM_IOCTL_START; @@ -2105,11 +2114,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); if (atomic_read(&substream->mmap_count)) { snd_pcm_sframes_t n; - n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; + delay = get_hw_ptr_period(runtime); + n = delay - runtime->oss.prev_hw_ptr_period; if (n < 0) n += runtime->boundary; info.blocks = n / runtime->period_size; - runtime->oss.prev_hw_ptr_interrupt = delay; + runtime->oss.prev_hw_ptr_period = delay; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_pcm_oss_simulate_fill(substream, delay); info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; @@ -2673,18 +2683,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_playback_avail(runtime) >= + runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_capture_avail(runtime) >= + runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6884ae031f6..df57a0e30bf 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -921,6 +921,10 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + if (runtime->hwptr_log) + kfree(runtime->hwptr_log); +#endif kfree(runtime); substream->runtime = NULL; put_pid(substream->pid); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a27545b23ee..0ee7e807c96 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -126,17 +126,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } -#ifdef CONFIG_SND_PCM_XRUN_DEBUG -#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) -#else -#define xrun_debug(substream, mask) 0 -#endif - -#define dump_stack_on_xrun(substream) do { \ - if (xrun_debug(substream, 2)) \ - dump_stack(); \ - } while (0) - static void pcm_debug_name(struct snd_pcm_substream *substream, char *name, size_t len) { @@ -147,6 +136,24 @@ static void pcm_debug_name(struct snd_pcm_substream *substream, substream->number); } +#define XRUN_DEBUG_BASIC (1<<0) +#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ +#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ +#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ +#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ +#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ +#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ + +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + +#define xrun_debug(substream, mask) \ + ((substream)->pstr->xrun_debug & (mask)) + +#define dump_stack_on_xrun(substream) do { \ + if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ + dump_stack(); \ + } while (0) + static void xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -154,7 +161,7 @@ static void xrun(struct snd_pcm_substream *substream) if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - if (xrun_debug(substream, 1)) { + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { char name[16]; pcm_debug_name(substream, name, sizeof(name)); snd_printd(KERN_DEBUG "XRUN: %s\n", name); @@ -162,32 +169,102 @@ static void xrun(struct snd_pcm_substream *substream) } } -static snd_pcm_uframes_t -snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) -{ +#define hw_ptr_error(substream, fmt, args...) \ + do { \ + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ + xrun_log_show(substream); \ + if (printk_ratelimit()) { \ + snd_printd("PCM: " fmt, ##args); \ + } \ + dump_stack_on_xrun(substream); \ + } \ + } while (0) + +#define XRUN_LOG_CNT 10 + +struct hwptr_log_entry { + unsigned long jiffies; snd_pcm_uframes_t pos; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t old_hw_ptr; + snd_pcm_uframes_t hw_ptr_base; +}; - pos = substream->ops->pointer(substream); - if (pos == SNDRV_PCM_POS_XRUN) - return pos; /* XRUN */ - if (pos >= runtime->buffer_size) { - if (printk_ratelimit()) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " - "buffer size = 0x%lx, period size = 0x%lx\n", - name, pos, runtime->buffer_size, - runtime->period_size); - } - pos = 0; +struct snd_pcm_hwptr_log { + unsigned int idx; + unsigned int hit: 1; + struct hwptr_log_entry entries[XRUN_LOG_CNT]; +}; + +static void xrun_log(struct snd_pcm_substream *substream, + snd_pcm_uframes_t pos) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hwptr_log *log = runtime->hwptr_log; + struct hwptr_log_entry *entry; + + if (log == NULL) { + log = kzalloc(sizeof(*log), GFP_ATOMIC); + if (log == NULL) + return; + runtime->hwptr_log = log; + } else { + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; } - pos -= pos % runtime->min_align; - return pos; + entry = &log->entries[log->idx]; + entry->jiffies = jiffies; + entry->pos = pos; + entry->period_size = runtime->period_size; + entry->buffer_size = runtime->buffer_size;; + entry->old_hw_ptr = runtime->status->hw_ptr; + entry->hw_ptr_base = runtime->hw_ptr_base; + log->idx = (log->idx + 1) % XRUN_LOG_CNT; } -static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) +static void xrun_log_show(struct snd_pcm_substream *substream) +{ + struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; + struct hwptr_log_entry *entry; + char name[16]; + unsigned int idx; + int cnt; + + if (log == NULL) + return; + if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) + return; + 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) + break; + snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " + "hwptr=%ld/%ld\n", + name, entry->jiffies, (unsigned long)entry->pos, + (unsigned long)entry->period_size, + (unsigned long)entry->buffer_size, + (unsigned long)entry->old_hw_ptr, + (unsigned long)entry->hw_ptr_base); + idx++; + idx %= XRUN_LOG_CNT; + } + log->hit = 1; +} + +#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ + +#define xrun_debug(substream, mask) 0 +#define xrun(substream) do { } while (0) +#define hw_ptr_error(substream, fmt, args...) do { } while (0) +#define xrun_log(substream, pos) do { } while (0) +#define xrun_log_show(substream) do { } while (0) + +#endif + +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t avail; @@ -208,89 +285,96 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, return -EPIPE; } } - if (avail >= runtime->control->avail_min) + if (!runtime->nowake && avail >= runtime->control->avail_min) wake_up(&runtime->sleep); return 0; } -#define hw_ptr_error(substream, fmt, args...) \ - do { \ - if (xrun_debug(substream, 1)) { \ - if (printk_ratelimit()) { \ - snd_printd("PCM: " fmt, ##args); \ - } \ - dump_stack_on_xrun(substream); \ - } \ - } while (0) - -static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) +static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, + unsigned int in_interrupt) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t pos; - snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_sframes_t hdelta, delta; unsigned long jdelta; old_hw_ptr = runtime->status->hw_ptr; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); + pos = substream->ops->pointer(substream); if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; } - if (xrun_debug(substream, 8)) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)old_hw_ptr, - (unsigned long)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); + if (pos >= runtime->buffer_size) { + if (printk_ratelimit()) { + char name[16]; + 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", + name, pos, runtime->buffer_size, + runtime->period_size); + } + pos = 0; } + pos -= pos % runtime->min_align; + if (xrun_debug(substream, XRUN_DEBUG_LOG)) + xrun_log(substream, pos); hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; - hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; - delta = new_hw_ptr - hw_ptr_interrupt; - if (hw_ptr_interrupt >= runtime->boundary) { - hw_ptr_interrupt -= runtime->boundary; - if (hw_base < runtime->boundary / 2) - /* hw_base was already lapped; recalc delta */ - delta = new_hw_ptr - hw_ptr_interrupt; - } - if (delta < 0) { - if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) - delta += runtime->buffer_size; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value " - "(stream=%i, pos=%ld, intr_ptr=%ld)\n", - substream->stream, (long)pos, - (long)hw_ptr_interrupt); -#if 1 - /* simply skipping the hwptr update seems more - * robust in some cases, e.g. on VMware with - * inaccurate timer source - */ - return 0; /* skip this update */ -#else - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; - delta = 0; -#endif - } else { + if (in_interrupt) { + /* we know that one period was processed */ + /* delta = "expected next hw_ptr" for in_interrupt != 0 */ + delta = old_hw_ptr - (old_hw_ptr % runtime->period_size) + + runtime->period_size; + if (delta > new_hw_ptr) { hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) hw_base = 0; new_hw_ptr = hw_base + pos; + goto __delta; } } + /* new_hw_ptr might be lower than old_hw_ptr in case when */ + /* pointer crosses the end of the ring buffer */ + if (new_hw_ptr < old_hw_ptr) { + hw_base += runtime->buffer_size; + if (hw_base >= runtime->boundary) + hw_base = 0; + new_hw_ptr = hw_base + pos; + } + __delta: + delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary; + if (xrun_debug(substream, in_interrupt ? + XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { + char name[16]; + 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", + name, + (unsigned int)pos, + (unsigned int)runtime->period_size, + (unsigned int)runtime->buffer_size, + (unsigned long)delta, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr, + (unsigned long)runtime->hw_ptr_base); + } + /* something must be really wrong */ + if (delta >= runtime->buffer_size + runtime->period_size) { + hw_ptr_error(substream, + "Unexpected hw_pointer value %s" + "(stream=%i, pos=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "[P]", + substream->stream, (long)pos, + (long)new_hw_ptr, (long)old_hw_ptr); + return 0; + } /* Do jiffies check only in xrun_debug mode */ - if (!xrun_debug(substream, 4)) + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check; /* Skip the jiffies check for hardwares with BATCH flag. @@ -299,7 +383,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) */ if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) goto no_jiffies_check; - hdelta = new_hw_ptr - old_hw_ptr; + hdelta = delta; if (hdelta < runtime->delay) goto no_jiffies_check; hdelta -= runtime->delay; @@ -308,130 +392,62 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) delta = jdelta / (((runtime->period_size * HZ) / runtime->rate) + HZ/100); + /* move new_hw_ptr according jiffies not pos variable */ + new_hw_ptr = old_hw_ptr; + /* use loop to avoid checks for delta overflows */ + /* the delta value is small or zero in most cases */ + while (delta > 0) { + new_hw_ptr += runtime->period_size; + if (new_hw_ptr >= runtime->boundary) + new_hw_ptr -= runtime->boundary; + delta--; + } + /* align hw_base to buffer_size */ + hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); + delta = 0; hw_ptr_error(substream, - "hw_ptr skipping! [Q] " + "hw_ptr skipping! %s" "(pos=%ld, delta=%ld, period=%ld, " - "jdelta=%lu/%lu/%lu)\n", + "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", + in_interrupt ? "[Q] " : "", (long)pos, (long)hdelta, (long)runtime->period_size, jdelta, - ((hdelta * HZ) / runtime->rate), delta); - hw_ptr_interrupt = runtime->hw_ptr_interrupt + - runtime->period_size * delta; - if (hw_ptr_interrupt >= runtime->boundary) - hw_ptr_interrupt -= runtime->boundary; - /* rebase to interrupt position */ - hw_base = new_hw_ptr = hw_ptr_interrupt; - /* align hw_base to buffer_size */ - hw_base -= hw_base % runtime->buffer_size; - delta = 0; + ((hdelta * HZ) / runtime->rate), delta, + (unsigned long)old_hw_ptr, + (unsigned long)new_hw_ptr); } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { hw_ptr_error(substream, - "Lost interrupts? " - "(stream=%i, delta=%ld, intr_ptr=%ld)\n", + "Lost interrupts? %s" + "(stream=%i, delta=%ld, new_hw_ptr=%ld, " + "old_hw_ptr=%ld)\n", + in_interrupt ? "[Q] " : "", substream->stream, (long)delta, - (long)hw_ptr_interrupt); - /* rebase hw_ptr_interrupt */ - hw_ptr_interrupt = - new_hw_ptr - new_hw_ptr % runtime->period_size; + (long)new_hw_ptr, + (long)old_hw_ptr); } - runtime->hw_ptr_interrupt = hw_ptr_interrupt; + + if (runtime->status->hw_ptr == new_hw_ptr) + return 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, new_hw_ptr); - if (runtime->status->hw_ptr == new_hw_ptr) - return 0; - runtime->hw_ptr_base = hw_base; runtime->status->hw_ptr = new_hw_ptr; runtime->hw_ptr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_state(substream, runtime); } /* CAUTION: call it with irq disabled */ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t pos; - snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; - snd_pcm_sframes_t delta; - unsigned long jdelta; - - old_hw_ptr = runtime->status->hw_ptr; - pos = snd_pcm_update_hw_ptr_pos(substream, runtime); - if (pos == SNDRV_PCM_POS_XRUN) { - xrun(substream); - return -EPIPE; - } - if (xrun_debug(substream, 16)) { - char name[16]; - pcm_debug_name(substream, name, sizeof(name)); - snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", - name, (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)old_hw_ptr, - (unsigned long)runtime->hw_ptr_base, - (unsigned long)runtime->hw_ptr_interrupt); - } - - hw_base = runtime->hw_ptr_base; - new_hw_ptr = hw_base + pos; - - delta = new_hw_ptr - old_hw_ptr; - jdelta = jiffies - runtime->hw_ptr_jiffies; - if (delta < 0) { - delta += runtime->buffer_size; - if (delta < 0) { - hw_ptr_error(substream, - "Unexpected hw_pointer value [2] " - "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", - substream->stream, (long)pos, - (long)old_hw_ptr, jdelta); - return 0; - } - hw_base += runtime->buffer_size; - if (hw_base >= runtime->boundary) - hw_base = 0; - new_hw_ptr = hw_base + pos; - } - /* Do jiffies check only in xrun_debug mode */ - if (!xrun_debug(substream, 4)) - goto no_jiffies_check; - if (delta < runtime->delay) - goto no_jiffies_check; - delta -= runtime->delay; - if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { - hw_ptr_error(substream, - "hw_ptr skipping! " - "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", - (long)pos, (long)delta, - (long)runtime->period_size, jdelta, - ((delta * HZ) / runtime->rate)); - return 0; - } - no_jiffies_check: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, new_hw_ptr); - - if (runtime->status->hw_ptr == new_hw_ptr) - return 0; - - runtime->hw_ptr_base = hw_base; - runtime->status->hw_ptr = new_hw_ptr; - runtime->hw_ptr_jiffies = jiffies; - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); - - return snd_pcm_update_hw_ptr_post(substream, runtime); + return snd_pcm_update_hw_ptr0(substream, 0); } /** @@ -1643,7 +1659,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || - snd_pcm_update_hw_ptr_interrupt(substream) < 0) + snd_pcm_update_hw_ptr0(substream, 1) < 0) goto _end; if (substream->timer_running) @@ -1776,6 +1792,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, goto _end_unlock; } + runtime->nowake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -1797,15 +1814,17 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { + runtime->nowake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -1834,8 +1853,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, } } _end_unlock: + runtime->nowake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } @@ -1993,6 +2014,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, goto _end_unlock; } + runtime->nowake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -2021,15 +2043,17 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { + runtime->nowake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) - goto _end; + err = transfer(substream, appl_ofs, data, offset, frames); snd_pcm_stream_lock_irq(substream); + if (err < 0) + goto _end_unlock; switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -2052,8 +2076,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, xfer += frames; } _end_unlock: + runtime->nowake = 0; + if (xfer > 0 && err >= 0) + snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); - _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 25b0641e6b8..a870fe69657 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -516,6 +516,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @@ -540,6 +541,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } + err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->period_step = params->period_step; @@ -553,10 +555,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - wake_up(&runtime->sleep); + err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); - return 0; + return err; } static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, @@ -1247,8 +1249,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) if (err < 0) return err; runtime->hw_ptr_base = 0; - runtime->hw_ptr_interrupt = runtime->status->hw_ptr - - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; return 0; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index d9266bae284..1caf5e3c1f6 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -544,25 +544,10 @@ static int patch_wolfson04(struct snd_ac97 * ac97) return 0; } -static int patch_wolfson_wm9705_specific(struct snd_ac97 * ac97) -{ - int err, i; - for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0) - return err; - } - snd_ac97_write_cache(ac97, 0x72, 0x0808); - return 0; -} - -static struct snd_ac97_build_ops patch_wolfson_wm9705_ops = { - .build_specific = patch_wolfson_wm9705_specific, -}; - static int patch_wolfson05(struct snd_ac97 * ac97) { /* WM9705, WM9710 */ - ac97->build_ops = &patch_wolfson_wm9705_ops; + ac97->build_ops = &patch_wolfson_wm9703_ops; #ifdef CONFIG_TOUCHSCREEN_WM9705 /* WM9705 touchscreen uses AUX and VIDEO for touch */ ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f98b47cd6cf..98767df4f03 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -824,6 +824,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, struct hda_pincfg *pin; unsigned int oldcfg; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return -EINVAL; + oldcfg = snd_hda_codec_get_pincfg(codec, nid); pin = look_up_pincfg(codec, list, nid); if (!pin) { @@ -899,6 +902,25 @@ static void restore_pincfgs(struct hda_codec *codec) } } +/** + * snd_hda_shutup_pins - Shut up all pins + * @codec: the HDA codec + * + * Clear all pin controls to shup up before suspend for avoiding click noise. + * The controls aren't cached so that they can be resumed properly. + */ +void snd_hda_shutup_pins(struct hda_codec *codec) +{ + int i; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + /* use read here for syncing after issuing each verb */ + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } +} +EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); + static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size); static void free_hda_cache(struct hda_cache_rec *cache); @@ -931,6 +953,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) #endif list_del(&codec->list); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -985,7 +1008,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); + snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1708,7 +1732,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); /** - * snd_hda_ctl-add - Add a control element and assign to the codec + * snd_hda_ctl_add - Add a control element and assign to the codec * @codec: HD-audio codec * @nid: corresponding NID (optional) * @kctl: the control element to assign @@ -1723,19 +1747,25 @@ EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); * * snd_hda_ctl_add() checks the control subdev id field whether * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower - * bits value is taken as the NID to assign. + * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit + * specifies if kctl->private_value is a HDA amplifier value. */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { int err; + unsigned short flags = 0; struct hda_nid_item *item; - if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { + if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { + flags |= HDA_NID_ITEM_AMP; if (nid == 0) - nid = kctl->id.subdevice & 0xffff; - kctl->id.subdevice = 0; + nid = get_amp_nid_(kctl->private_value); } + if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) + nid = kctl->id.subdevice & 0xffff; + if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) + kctl->id.subdevice = 0; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; @@ -1744,11 +1774,41 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, return -ENOMEM; item->kctl = kctl; item->nid = nid; + item->flags = flags; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); /** + * snd_hda_add_nid - Assign a NID to a control element + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * @index: index to kctl + * + * Add the given control element to an array inside the codec instance. + * This function is used when #snd_hda_ctl_add cannot be used for 1:1 + * NID:KCTL mapping - for example "Capture Source" selector. + */ +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid) +{ + struct hda_nid_item *item; + + if (nid > 0) { + item = snd_array_new(&codec->nids); + if (!item) + return -ENOMEM; + item->kctl = kctl; + item->index = index; + item->nid = nid; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL_HDA(snd_hda_add_nid); + +/** * snd_hda_ctls_clear - Clear all controls assigned to the given codec * @codec: HD-audio codec */ @@ -1759,6 +1819,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); } /* pseudo device locking @@ -3214,6 +3275,8 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { /* * get the empty PCM device number to assign + * + * note the max device number is limited by HDA_MAX_PCMS, currently 10 */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { @@ -3478,6 +3541,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) for (; knew->name; knew++) { struct snd_kcontrol *kctl; + if (knew->iface == -1) /* skip this codec private value */ + continue; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0a770a28e71..b75da47571e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -527,6 +527,9 @@ enum { /* max. codec address */ #define HDA_MAX_CODEC_ADDRESS 0x0f +/* max number of PCM devics per card */ +#define HDA_MAX_PCMS 10 + /* * generic arrays */ @@ -789,6 +792,7 @@ struct hda_codec { u32 *wcaps; struct snd_array mixers; /* list of assigned mixer elements */ + struct snd_array nids; /* list of mapped mixer elements */ struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ @@ -898,6 +902,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg); 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); /* * Mixer diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 092c6a7c2ff..5ea21285ee1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -861,7 +861,8 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec)); + err = snd_hda_ctl_add(codec, spec->adc_node->nid, + snd_ctl_new1(&cap_sel, codec)); if (err < 0) return err; diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 40ccb419b6e..a1fc83753cc 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -293,8 +293,11 @@ static ssize_t type##_store(struct device *dev, \ { \ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ struct hda_codec *codec = hwdep->private_data; \ - char *after; \ - codec->type = simple_strtoul(buf, &after, 0); \ + unsigned long val; \ + int err = strict_strtoul(buf, 0, &val); \ + if (err < 0) \ + return err; \ + codec->type = val; \ return count; \ } @@ -622,6 +625,10 @@ enum { LINE_MODE_PINCFG, LINE_MODE_VERB, LINE_MODE_HINT, + LINE_MODE_VENDOR_ID, + LINE_MODE_SUBSYSTEM_ID, + LINE_MODE_REVISION_ID, + LINE_MODE_CHIP_NAME, NUM_LINE_MODES, }; @@ -651,53 +658,71 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, } /* parse the contents after the other command tags, [pincfg], [verb], - * [hint] and [model] + * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model] * just pass to the sysfs helper (only when any codec was specified) */ static void parse_pincfg_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_user_pin_configs(*codecp, buf); } static void parse_verb_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_init_verbs(*codecp, buf); } static void parse_hint_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_hints(*codecp, buf); } static void parse_model_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; kfree((*codecp)->modelname); (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); } +static void parse_chip_name_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + kfree((*codecp)->chip_name); + (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); +} + +#define DEFINE_PARSE_ID_MODE(name) \ +static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ + struct hda_codec **codecp) \ +{ \ + unsigned long val; \ + if (!strict_strtoul(buf, 0, &val)) \ + (*codecp)->name = val; \ +} + +DEFINE_PARSE_ID_MODE(vendor_id); +DEFINE_PARSE_ID_MODE(subsystem_id); +DEFINE_PARSE_ID_MODE(revision_id); + + struct hda_patch_item { const char *tag; void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); + int need_codec; }; static struct hda_patch_item patch_items[NUM_LINE_MODES] = { - [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode }, - [LINE_MODE_MODEL] = { "[model]", parse_model_mode }, - [LINE_MODE_VERB] = { "[verb]", parse_verb_mode }, - [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode }, - [LINE_MODE_HINT] = { "[hint]", parse_hint_mode }, + [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 }, + [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 }, + [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 }, + [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 }, + [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 }, + [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 }, + [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 }, + [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 }, + [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 }, }; /* check the line starting with '[' -- change the parser mode accodingly */ @@ -780,7 +805,8 @@ int snd_hda_load_patch(struct hda_bus *bus, const char *patch) continue; if (*buf == '[') line_mode = parse_line_mode(buf, bus); - else if (patch_items[line_mode].parser) + else if (patch_items[line_mode].parser && + (codec || !patch_items[line_mode].need_codec)) patch_items[line_mode].parser(buf, bus, &codec); } release_firmware(fw); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b8faa6dc5ab..353412bb5cc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -125,6 +125,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH9}," "{Intel, ICH10}," "{Intel, PCH}," + "{Intel, CPT}," "{Intel, SCH}," "{ATI, SB450}," "{ATI, SB600}," @@ -259,8 +260,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_MAX_FRAG 32 /* max buffer size - no h/w limit, you can increase as you like */ #define AZX_MAX_BUF_SIZE (1024*1024*1024) -/* max number of PCM devics per card */ -#define AZX_MAX_PCMS 8 /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 @@ -408,7 +407,7 @@ struct azx { struct azx_dev *azx_dev; /* PCM */ - struct snd_pcm *pcm[AZX_MAX_PCMS]; + struct snd_pcm *pcm[HDA_MAX_PCMS]; /* HD codec */ unsigned short codec_mask; @@ -968,8 +967,8 @@ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) azx_dev->insufficient = 1; /* enable SIE */ - azx_writeb(chip, INTCTL, - azx_readb(chip, INTCTL) | (1 << azx_dev->index)); + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) | (1 << azx_dev->index)); /* set DMA start and interrupt mask */ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_DMA_START | SD_INT_MASK); @@ -988,8 +987,8 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) { azx_stream_clear(chip, azx_dev); /* disable SIE */ - azx_writeb(chip, INTCTL, - azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); } @@ -1350,7 +1349,7 @@ static void azx_bus_reset(struct hda_bus *bus) if (chip->initialized) { int i; - for (i = 0; i < AZX_MAX_PCMS; i++) + for (i = 0; i < HDA_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); snd_hda_suspend(chip->bus); snd_hda_resume(chip->bus); @@ -1980,7 +1979,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, int pcm_dev = cpcm->device; int s, err; - if (pcm_dev >= AZX_MAX_PCMS) { + if (pcm_dev >= HDA_MAX_PCMS) { snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", pcm_dev); return -EINVAL; @@ -2136,7 +2135,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); - for (i = 0; i < AZX_MAX_PCMS; i++) + for (i = 0; i < HDA_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); if (chip->initialized) snd_hda_suspend(chip->bus); @@ -2693,6 +2692,8 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, /* PCH */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH }, + /* CPT */ + { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_ICH }, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, /* ATI SB 450/600 */ @@ -2720,32 +2721,10 @@ static struct pci_device_id azx_ids[] = { /* ULI M5461 */ { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, /* NVIDIA MCP */ - { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0590), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_NVIDIA }, /* Teradici */ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, /* Creative X-Fi (CA0110-IBG) */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5778ae882b8..7cee364976f 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -31,6 +31,7 @@ * in snd_hda_ctl_add(), so that this value won't appear in the outside. */ #define HDA_SUBDEV_NID_FLAG (1U << 31) +#define HDA_SUBDEV_AMP_FLAG (1U << 30) /* * for mixer controls @@ -42,7 +43,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -63,7 +64,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -81,7 +82,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ @@ -464,13 +465,20 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); +/* flags for hda_nid_item */ +#define HDA_NID_ITEM_AMP (1<<0) + struct hda_nid_item { struct snd_kcontrol *kctl; + unsigned int index; hda_nid_t nid; + unsigned short flags; }; int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl); +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid); void snd_hda_ctls_clear(struct hda_codec *codec); /* diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index c9afc04adac..f97d35de66c 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -61,18 +61,29 @@ static const char *get_wid_type_name(unsigned int wid_value) return "UNKNOWN Widget"; } -static void print_nid_mixers(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) +static void print_nid_array(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + struct snd_array *array) { int i; - struct hda_nid_item *items = codec->mixers.list; + struct hda_nid_item *items = array->list, *item; struct snd_kcontrol *kctl; - for (i = 0; i < codec->mixers.used; i++) { - if (items[i].nid == nid) { - kctl = items[i].kctl; + for (i = 0; i < array->used; i++) { + item = &items[i]; + if (item->nid == nid) { + kctl = item->kctl; snd_iprintf(buffer, " Control: name=\"%s\", index=%i, device=%i\n", - kctl->id.name, kctl->id.index, kctl->id.device); + kctl->id.name, kctl->id.index + item->index, + kctl->id.device); + if (item->flags & HDA_NID_ITEM_AMP) + snd_iprintf(buffer, + " ControlAmp: chs=%lu, dir=%s, " + "idx=%lu, ofs=%lu\n", + get_amp_channels(kctl), + get_amp_direction(kctl) ? "Out" : "In", + get_amp_index(kctl), + get_amp_offset(kctl)); } } } @@ -528,7 +539,8 @@ static void print_gpio(struct snd_info_buffer *buffer, (data & (1<<i)) ? 1 : 0, (unsol & (1<<i)) ? 1 : 0); /* FIXME: add GPO and GPI pin information */ - print_nid_mixers(buffer, codec, nid); + print_nid_array(buffer, codec, nid, &codec->mixers); + print_nid_array(buffer, codec, nid, &codec->nids); } static void print_codec_info(struct snd_info_entry *entry, @@ -608,7 +620,8 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, " CP"); snd_iprintf(buffer, "\n"); - print_nid_mixers(buffer, codec, nid); + print_nid_array(buffer, codec, nid, &codec->mixers); + print_nid_array(buffer, codec, nid, &codec->nids); print_nid_pcms(buffer, codec, nid); /* volume knob is a special widget that always have connection diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 69a941c7b15..21011b5199d 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -174,6 +174,7 @@ static struct snd_kcontrol_new ad_beep_mixer[] = { static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + struct snd_kcontrol *kctl; unsigned int i; int err; @@ -208,9 +209,7 @@ static int ad198x_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, - get_amp_nid_(spec->beep_amp), - kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } @@ -239,6 +238,27 @@ static int ad198x_build_controls(struct hda_codec *codec) } ad198x_free_kctls(codec); /* no longer needed */ + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + if (!kctl) + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]); + if (err < 0) + return err; + } + + /* assign IEC958 enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, + SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source"); + if (kctl) { + err = snd_hda_add_nid(codec, kctl, 0, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + return 0; } @@ -421,6 +441,11 @@ static int ad198x_build_pcms(struct hda_codec *codec) return 0; } +static inline void ad198x_shutup(struct hda_codec *codec) +{ + snd_hda_shutup_pins(codec); +} + static void ad198x_free_kctls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -434,6 +459,46 @@ static void ad198x_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, + hda_nid_t hp) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, + !spec->inv_eapd ? 0x00 : 0x02); + snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, + !spec->inv_eapd ? 0x00 : 0x02); +} + +static void ad198x_power_eapd(struct hda_codec *codec) +{ + /* We currently only handle front, HP */ + switch (codec->vendor_id) { + case 0x11d41882: + case 0x11d4882a: + case 0x11d41884: + case 0x11d41984: + case 0x11d41883: + case 0x11d4184a: + case 0x11d4194a: + case 0x11d4194b: + ad198x_power_eapd_write(codec, 0x12, 0x11); + break; + case 0x11d41981: + case 0x11d41983: + ad198x_power_eapd_write(codec, 0x05, 0x06); + break; + case 0x11d41986: + ad198x_power_eapd_write(codec, 0x1b, 0x1a); + break; + case 0x11d41988: + case 0x11d4198b: + case 0x11d4989a: + case 0x11d4989b: + ad198x_power_eapd_write(codec, 0x29, 0x22); + break; + } +} + static void ad198x_free(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -441,11 +506,29 @@ static void ad198x_free(struct hda_codec *codec) if (!spec) return; + ad198x_shutup(codec); ad198x_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); } +#ifdef SND_HDA_NEEDS_RESUME +static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) +{ + ad198x_shutup(codec); + ad198x_power_eapd(codec); + return 0; +} + +static int ad198x_resume(struct hda_codec *codec) +{ + ad198x_init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + return 0; +} +#endif + static struct hda_codec_ops ad198x_patch_ops = { .build_controls = ad198x_build_controls, .build_pcms = ad198x_build_pcms, @@ -454,6 +537,11 @@ static 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 + .suspend = ad198x_suspend, + .resume = ad198x_resume, +#endif + .reboot_notify = ad198x_shutup, }; @@ -701,6 +789,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "External Amplifier", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, @@ -808,6 +897,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { { .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 = ad1986a_hp_master_sw_put, @@ -1612,6 +1702,7 @@ static struct snd_kcontrol_new ad1981_hp_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_NID_FLAG | 0x05, .name = "Master Playback Switch", .info = ad198x_eapd_info, .get = ad198x_eapd_get, @@ -2136,6 +2227,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "External Amplifier", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x12, .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, @@ -2257,6 +2349,7 @@ static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "IEC958 Playback Source", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, .info = ad1988_spdif_playback_source_info, .get = ad1988_spdif_playback_source_get, .put = ad1988_spdif_playback_source_put, @@ -2589,7 +2682,7 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, if (! knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } @@ -3747,6 +3840,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { { .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 = ad1884a_mobile_master_sw_put, @@ -3775,6 +3869,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { { .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 = ad1884a_mobile_master_sw_put, @@ -4116,6 +4211,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_AMP_FLAG, .name = "Master Playback Switch", .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index fe0423c3959..7de782a5b8f 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -501,7 +501,8 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); } static int add_volume(struct hda_codec *codec, const char *name, @@ -514,7 +515,8 @@ static int add_volume(struct hda_codec *codec, const char *name, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); } static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) @@ -751,6 +753,7 @@ static int build_input(struct hda_codec *codec) 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); @@ -760,6 +763,13 @@ static int build_input(struct hda_codec *codec) 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[i]); + if (err < 0) + return err; + } } if (spec->num_inputs > 1 && !spec->mic_detect) { diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index a45c1169762..ff60908f455 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -315,7 +315,8 @@ static struct hda_verb cmi9880_allout_init[] = { static int cmi9880_build_controls(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; - int err; + struct snd_kcontrol *kctl; + int i, err; err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer); if (err < 0) @@ -340,6 +341,14 @@ static int cmi9880_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]); + if (err < 0) + return err; + } return 0; } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index c578c28f368..745e3599214 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -42,10 +42,12 @@ /* Conexant 5051 specific */ -#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_SPDIF_OUT 0x12 #define CXT5051_PORTB_EVENT 0x38 #define CXT5051_PORTC_EVENT 0x39 +#define AUTO_MIC_PORTB (1 << 1) +#define AUTO_MIC_PORTC (1 << 2) struct conexant_jack { @@ -74,7 +76,7 @@ struct conexant_spec { */ unsigned int cur_eapd; unsigned int hp_present; - unsigned int no_auto_mic; + unsigned int auto_mic; unsigned int need_dac_fix; /* capture */ @@ -111,8 +113,22 @@ struct conexant_spec { unsigned int dell_automute; unsigned int port_d_mode; - unsigned char ext_mic_bias; unsigned int dell_vostro; + + unsigned int ext_mic_present; + unsigned int recording; + void (*capture_prepare)(struct hda_codec *codec); + void (*capture_cleanup)(struct hda_codec *codec); + + /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) + * through the microphone jack. + * When the user enables this through a mixer switch, both internal and + * external microphones are disabled. Gain is fixed at 0dB. In this mode, + * we also allow the bias to be configured through a separate mixer + * control. */ + unsigned int dc_enable; + unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ + unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -185,6 +201,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; + if (spec->capture_prepare) + spec->capture_prepare(codec); snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], stream_tag, 0, format); return 0; @@ -196,6 +214,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct conexant_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); + if (spec->capture_cleanup) + spec->capture_cleanup(codec); return 0; } @@ -1585,6 +1605,11 @@ static void cxt5051_update_speaker(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; unsigned int pinctl; + /* headphone pin */ + pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); + /* speaker pin */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); @@ -1608,7 +1633,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int present; - if (spec->no_auto_mic) + if (!(spec->auto_mic & AUTO_MIC_PORTB)) return; present = snd_hda_jack_detect(codec, 0x17); snd_hda_codec_write(codec, 0x14, 0, @@ -1623,7 +1648,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) unsigned int present; hda_nid_t new_adc; - if (spec->no_auto_mic) + if (!(spec->auto_mic & AUTO_MIC_PORTC)) return; present = snd_hda_jack_detect(codec, 0x18); if (present) @@ -1669,13 +1694,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, conexant_report_jack(codec, nid); } -static struct snd_kcontrol_new cxt5051_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), +static struct snd_kcontrol_new cxt5051_playback_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1685,7 +1704,16 @@ static struct snd_kcontrol_new cxt5051_mixers[] = { .put = cxt5051_hp_master_sw_put, .private_value = 0x1a, }, + {} +}; +static struct snd_kcontrol_new cxt5051_capture_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), {} }; @@ -1694,32 +1722,26 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = { HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT), HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} }; static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { - HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT), + {} +}; +static struct snd_kcontrol_new cxt5051_f700_mixers[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT), + {} +}; + +static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), {} }; @@ -1748,8 +1770,6 @@ static struct hda_verb cxt5051_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, { } /* end */ }; @@ -1775,7 +1795,6 @@ static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, { } /* end */ }; @@ -1807,17 +1826,60 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, { } /* end */ }; +static struct hda_verb cxt5051_f700_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + { } /* end */ +}; + +static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, + unsigned int event) +{ + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | event); +#ifdef CONFIG_SND_HDA_INPUT_JACK + conexant_add_jack(codec, nid, SND_JACK_MICROPHONE); + conexant_report_jack(codec, nid); +#endif +} + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; + conexant_init(codec); conexant_init_jacks(codec); + + if (spec->auto_mic & AUTO_MIC_PORTB) + cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT); + if (spec->auto_mic & AUTO_MIC_PORTC) + cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT); + if (codec->patch_ops.unsol_event) { cxt5051_hp_automute(codec); cxt5051_portb_automic(codec); @@ -1832,6 +1894,8 @@ enum { CXT5051_HP, /* no docking */ CXT5051_HP_DV6736, /* HP without mic switch */ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ + CXT5051_F700, /* HP Compaq Presario F700 */ + CXT5051_TOSHIBA, /* Toshiba M300 & co */ CXT5051_MODELS }; @@ -1840,11 +1904,15 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_HP] = "hp", [CXT5051_HP_DV6736] = "hp-dv6736", [CXT5051_LENOVO_X200] = "lenovo-x200", + [CXT5051_F700] = "hp-700", + [CXT5051_TOSHIBA] = "toshiba", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), + SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), + SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), @@ -1872,8 +1940,9 @@ static int patch_cxt5051(struct hda_codec *codec) spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; spec->num_adc_nids = 1; /* not 2; via auto-mic switch */ spec->adc_nids = cxt5051_adc_nids; - spec->num_mixers = 1; - spec->mixers[0] = cxt5051_mixers; + spec->num_mixers = 2; + spec->mixers[0] = cxt5051_capture_mixers; + spec->mixers[1] = cxt5051_playback_mixers; spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5051_init_verbs; spec->spdif_route = 0; @@ -1887,6 +1956,7 @@ static int patch_cxt5051(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, cxt5051_models, cxt5051_cfg_tbl); + spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC; switch (board_config) { case CXT5051_HP: spec->mixers[0] = cxt5051_hp_mixers; @@ -1894,11 +1964,20 @@ static int patch_cxt5051(struct hda_codec *codec) case CXT5051_HP_DV6736: spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs; spec->mixers[0] = cxt5051_hp_dv6736_mixers; - spec->no_auto_mic = 1; + spec->auto_mic = 0; break; case CXT5051_LENOVO_X200: spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; break; + case CXT5051_F700: + spec->init_verbs[0] = cxt5051_f700_init_verbs; + spec->mixers[0] = cxt5051_f700_mixers; + spec->auto_mic = 0; + break; + case CXT5051_TOSHIBA: + spec->mixers[0] = cxt5051_toshiba_mixers; + spec->auto_mic = AUTO_MIC_PORTB; + break; } return 0; @@ -1966,53 +2045,97 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, return 1; } -/* toggle input of built-in and mic jack appropriately */ -static void cxt5066_automic(struct hda_codec *codec) +static const struct hda_input_mux cxt5066_olpc_dc_bias = { + .num_items = 3, + .items = { + { "Off", PIN_IN }, + { "50%", PIN_VREF50 }, + { "80%", PIN_VREF80 }, + }, +}; + +static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - struct hda_verb ext_mic_present[] = { - /* enable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, + /* Even though port F is the DC input, the bias is controlled on port B. + * we also leave that port as an active input (but unselected) in DC mode + * just in case that is necessary to make the bias setting take effect. */ + return snd_hda_codec_write_cache(codec, 0x1a, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); +} - /* switch to external mic input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0}, +/* OLPC defers mic widget control until when capture is started because the + * microphone LED comes on as soon as these settings are put in place. if we + * did this before recording, it would give the false indication that recording + * is happening when it is not. */ +static void cxt5066_olpc_select_mic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + if (!spec->recording) + return; - /* disable internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static struct hda_verb ext_mic_absent[] = { - /* enable internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + if (spec->dc_enable) { + /* in DC mode we ignore presence detection and just use the jack + * through our special DC port */ + const struct hda_verb enable_dc_mode[] = { + /* disble internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* enable DC capture, port F */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {}, + }; + + snd_hda_sequence_write(codec, enable_dc_mode); + /* port B input disabled (and bias set) through the following call */ + cxt5066_set_olpc_dc_bias(codec); + return; + } - /* switch to internal mic input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 1}, + /* disable DC (port F) */ + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - /* disable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; + /* external mic, port B */ + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); + + /* internal mic, port C */ + snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->ext_mic_present ? 0 : PIN_VREF80); +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5066_olpc_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; unsigned int present; - present = snd_hda_jack_detect(codec, 0x1a); - if (present) { + if (spec->dc_enable) /* don't do presence detection in DC mode */ + return; + + present = snd_hda_codec_read(codec, 0x1a, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) snd_printdd("CXT5066: external microphone detected\n"); - snd_hda_sequence_write(codec, ext_mic_present); - } else { + else snd_printdd("CXT5066: external microphone absent\n"); - snd_hda_sequence_write(codec, ext_mic_absent); - } + + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, + present ? 0 : 1); + spec->ext_mic_present = !!present; + + cxt5066_olpc_select_mic(codec); } /* toggle input of built-in digital mic and mic jack appropriately */ static void cxt5066_vostro_automic(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; unsigned int present; struct hda_verb ext_mic_present[] = { /* enable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* switch to external mic input */ {0x17, AC_VERB_SET_CONNECT_SEL, 0}, @@ -2063,15 +2186,18 @@ static void cxt5066_hp_automute(struct hda_codec *codec) } /* unsolicited event for jack sensing */ -static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) +static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) { + struct conexant_spec *spec = codec->spec; snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5066_hp_automute(codec); break; case CONEXANT_MIC_EVENT: - cxt5066_automic(codec); + /* ignore mic events in DC mode; we're always using the jack */ + if (!spec->dc_enable) + cxt5066_olpc_automic(codec); break; } } @@ -2101,6 +2227,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = { }, }; +static int cxt5066_set_mic_boost(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | + cxt5066_analog_mic_boost.items[spec->mic_boost].index); +} + static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2111,15 +2246,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int val; - hda_nid_t nid = kcontrol->private_value & 0xff; - int inout = (kcontrol->private_value & 0x100) ? - AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT; - - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, inout); - - ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; + struct conexant_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->mic_boost; return 0; } @@ -2127,26 +2255,132 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; unsigned int idx; - hda_nid_t nid = kcontrol->private_value & 0xff; - int inout = (kcontrol->private_value & 0x100) ? - AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT; + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + + spec->mic_boost = idx; + if (!spec->dc_enable) + cxt5066_set_mic_boost(codec); + return 1; +} - if (!imux->num_items) +static void cxt5066_enable_dc(struct hda_codec *codec) +{ + const struct hda_verb enable_dc_mode[] = { + /* disable gain */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* switch to DC input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 3}, + {} + }; + + /* configure as input source */ + snd_hda_sequence_write(codec, enable_dc_mode); + cxt5066_olpc_select_mic(codec); /* also sets configured bias */ +} + +static void cxt5066_disable_dc(struct hda_codec *codec) +{ + /* reconfigure input source */ + cxt5066_set_mic_boost(codec); + /* automic also selects the right mic if we're recording */ + cxt5066_olpc_automic(codec); +} + +static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + ucontrol->value.integer.value[0] = spec->dc_enable; + return 0; +} + +static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + int dc_enable = !!ucontrol->value.integer.value[0]; + + if (dc_enable == spec->dc_enable) return 0; + + spec->dc_enable = dc_enable; + if (dc_enable) + cxt5066_enable_dc(codec); + else + cxt5066_disable_dc(codec); + + return 1; +} + +static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo); +} + +static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->dc_input_bias; + return 0; +} + +static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; + unsigned int idx; + idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout | - imux->items[idx].index); - + spec->dc_input_bias = idx; + if (spec->dc_enable) + cxt5066_set_olpc_dc_bias(codec); return 1; } +static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + /* mark as recording and configure the microphone widget so that the + * recording LED comes on. */ + spec->recording = 1; + cxt5066_olpc_select_mic(codec); +} + +static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + const struct hda_verb disable_mics[] = { + /* disable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* disble internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* disable DC capture, port F */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {}, + }; + + snd_hda_sequence_write(codec, disable_mics); + spec->recording = 0; +} + static struct hda_input_mux cxt5066_capture_source = { .num_items = 4, .items = { @@ -2187,6 +2421,7 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = snd_hda_mixer_amp_volume_put, @@ -2198,6 +2433,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { {} }; +static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Mode Enable Switch", + .info = snd_ctl_boolean_mono_info, + .get = cxt5066_olpc_dc_get, + .put = cxt5066_olpc_dc_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Input Bias Enum", + .info = cxt5066_olpc_dc_bias_enum_info, + .get = cxt5066_olpc_dc_bias_enum_get, + .put = cxt5066_olpc_dc_bias_enum_put, + }, + {} +}; + static struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2210,11 +2463,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Ext Mic Boost Capture Enum", + .name = "Analog Mic Boost Capture Enum", .info = cxt5066_mic_boost_mux_enum_info, .get = cxt5066_mic_boost_mux_enum_get, .put = cxt5066_mic_boost_mux_enum_put, - .private_value = 0x17, }, HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), @@ -2296,10 +2548,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ /* Port B: external microphone */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port C: internal microphone */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port D: unused */ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, @@ -2308,7 +2560,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - /* Port F: unused */ + /* Port F: external DC input through microphone port */ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port G: internal speakers */ @@ -2428,8 +2680,22 @@ static int cxt5066_init(struct hda_codec *codec) cxt5066_hp_automute(codec); if (spec->dell_vostro) cxt5066_vostro_automic(codec); - else - cxt5066_automic(codec); + } + cxt5066_set_mic_boost(codec); + return 0; +} + +static int cxt5066_olpc_init(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + snd_printdd("CXT5066: init\n"); + conexant_init(codec); + cxt5066_hp_automute(codec); + if (!spec->dc_enable) { + cxt5066_set_mic_boost(codec); + cxt5066_olpc_automic(codec); + } else { + cxt5066_enable_dc(codec); } return 0; } @@ -2470,7 +2736,7 @@ static int patch_cxt5066(struct hda_codec *codec) codec->spec = spec; codec->patch_ops = conexant_patch_ops; - codec->patch_ops.init = cxt5066_init; + codec->patch_ops.init = conexant_init; spec->dell_automute = 0; spec->multiout.max_channels = 2; @@ -2483,7 +2749,6 @@ static int patch_cxt5066(struct hda_codec *codec) spec->input_mux = &cxt5066_capture_source; spec->port_d_mode = PIN_HP; - spec->ext_mic_bias = PIN_VREF80; spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5066_init_verbs; @@ -2510,20 +2775,28 @@ static int patch_cxt5066(struct hda_codec *codec) spec->dell_automute = 1; break; case CXT5066_OLPC_XO_1_5: - codec->patch_ops.unsol_event = cxt5066_unsol_event; + codec->patch_ops.init = cxt5066_olpc_init; + codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; spec->init_verbs[0] = cxt5066_init_verbs_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->port_d_mode = 0; - spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS; + spec->mic_boost = 3; /* default 30dB gain */ /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; /* input source automatically selected */ spec->input_mux = NULL; + + /* our capture hooks which allow us to turn on the microphone LED + * at the right time */ + spec->capture_prepare = cxt5066_olpc_capture_prepare; + spec->capture_cleanup = cxt5066_olpc_capture_cleanup; break; case CXT5066_DELL_VOSTO: + codec->patch_ops.init = cxt5066_init; codec->patch_ops.unsol_event = cxt5066_vostro_event; spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; @@ -2531,6 +2804,7 @@ static int patch_cxt5066(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; spec->port_d_mode = 0; spec->dell_vostro = 1; + spec->mic_boost = 3; /* default 30dB gain */ snd_hda_attach_beep_device(codec, 0x13); /* no S/PDIF out */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index da34095c707..0c224977c8c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -131,8 +131,10 @@ enum { enum { ALC269_BASIC, ALC269_QUANTA_FL1, - ALC269_ASUS_AMIC, - ALC269_ASUS_DMIC, + ALC269_AMIC, + ALC269_DMIC, + ALC269VB_AMIC, + ALC269VB_DMIC, ALC269_FUJITSU, ALC269_LIFEBOOK, ALC269_AUTO, @@ -338,7 +340,7 @@ struct alc_spec { void (*init_hook)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); #ifdef CONFIG_SND_HDA_POWER_SAVE - void (*power_hook)(struct hda_codec *codec, int power); + void (*power_hook)(struct hda_codec *codec); #endif /* for pin sensing */ @@ -391,7 +393,7 @@ struct alc_config_preset { void (*init_hook)(struct hda_codec *); #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_amp_list *loopbacks; - void (*power_hook)(struct hda_codec *codec, int power); + void (*power_hook)(struct hda_codec *codec); #endif }; @@ -633,6 +635,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, #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, \ @@ -684,6 +687,7 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, } #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, \ @@ -738,6 +742,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, } #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, \ @@ -791,6 +796,7 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, #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, \ @@ -837,27 +843,6 @@ static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) spec->init_verbs[spec->num_init_verbs++] = verb; } -#ifdef CONFIG_PROC_FS -/* - * hook for proc - */ -static void print_realtek_coef(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int coeff; - - if (nid != 0x20) - return; - coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); - coeff = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_COEF_INDEX, 0); - snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); -} -#else -#define print_realtek_coef NULL -#endif - /* * set up from the preset table */ @@ -1162,6 +1147,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0888: alc888_coef_init(codec); break; +#if 0 /* XXX: This may cause the silent output on speaker on some machines */ case 0x10ec0267: case 0x10ec0268: snd_hda_codec_write(codec, 0x20, 0, @@ -1174,6 +1160,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) AC_VERB_SET_PROC_COEF, tmp | 0x3000); break; +#endif /* XXX */ } break; } @@ -1840,14 +1827,6 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[2] = 0x1b; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void alc889_power_eapd(struct hda_codec *codec, int power) -{ - set_eapd(codec, 0x14, power); - set_eapd(codec, 0x15, power); -} -#endif - /* * ALC880 3-stack model * @@ -2450,6 +2429,15 @@ static const char *alc_slave_sws[] = { * build control elements */ +#define NID_MAPPING (-1) + +#define SUBDEV_SPEAKER_ (0 << 6) +#define SUBDEV_HP_ (1 << 6) +#define SUBDEV_LINE_ (2 << 6) +#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) +#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) +#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) + static void alc_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -2464,8 +2452,11 @@ static struct snd_kcontrol_new alc_beep_mixer[] = { static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int err; - int i; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new *knew; + int i, j, err; + unsigned int u; + hda_nid_t nid; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -2506,8 +2497,7 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, - get_amp_nid_(spec->beep_amp), kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } @@ -2534,6 +2524,75 @@ static int alc_build_controls(struct hda_codec *codec) } alc_free_kctls(codec); /* no longer needed */ + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + if (!kctl) + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + hda_nid_t *nids = spec->capsrc_nids; + if (!nids) + nids = spec->adc_nids; + err = snd_hda_add_nid(codec, kctl, i, nids[i]); + if (err < 0) + return err; + } + if (spec->cap_mixer) { + const char *kname = kctl ? kctl->id.name : NULL; + for (knew = spec->cap_mixer; knew->name; knew++) { + if (kname && strcmp(knew->name, kname) == 0) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, + spec->adc_nids[i]); + if (err < 0) + 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; + u = knew->subdevice; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0x3f; + if (nid == 0) + continue; + switch (u & 0xc0) { + case SUBDEV_SPEAKER_: + nid = spec->autocfg.speaker_pins[nid]; + break; + case SUBDEV_LINE_: + nid = spec->autocfg.line_out_pins[nid]; + break; + case SUBDEV_HP_: + nid = spec->autocfg.hp_pins[nid]; + break; + default: + continue; + } + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + u = knew->private_value; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0xff; + if (nid == 0) + continue; + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + } + } return 0; } @@ -3616,6 +3675,11 @@ static int alc_build_pcms(struct hda_codec *codec) return 0; } +static inline void alc_shutup(struct hda_codec *codec) +{ + snd_hda_shutup_pins(codec); +} + static void alc_free_kctls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3636,17 +3700,44 @@ static void alc_free(struct hda_codec *codec) if (!spec) return; + alc_shutup(codec); alc_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE +static void alc_power_eapd(struct hda_codec *codec) +{ + /* We currently only handle front, HP */ + switch (codec->vendor_id) { + case 0x10ec0260: + set_eapd(codec, 0x0f, 0); + set_eapd(codec, 0x10, 0); + break; + case 0x10ec0262: + case 0x10ec0267: + case 0x10ec0268: + case 0x10ec0269: + case 0x10ec0270: + case 0x10ec0272: + case 0x10ec0660: + case 0x10ec0662: + case 0x10ec0663: + case 0x10ec0862: + case 0x10ec0889: + set_eapd(codec, 0x14, 0); + set_eapd(codec, 0x15, 0); + break; + } +} + static int alc_suspend(struct hda_codec *codec, pm_message_t state) { struct alc_spec *spec = codec->spec; + alc_shutup(codec); if (spec && spec->power_hook) - spec->power_hook(codec, 0); + spec->power_hook(codec); return 0; } #endif @@ -3654,16 +3745,9 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state) #ifdef SND_HDA_NEEDS_RESUME static int alc_resume(struct hda_codec *codec) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct alc_spec *spec = codec->spec; -#endif codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (spec && spec->power_hook) - spec->power_hook(codec, 1); -#endif return 0; } #endif @@ -3683,6 +3767,7 @@ static struct hda_codec_ops alc_patch_ops = { .suspend = alc_suspend, .check_power_status = alc_check_power_status, #endif + .reboot_notify = alc_shutup, }; @@ -3839,6 +3924,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, #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, \ @@ -3848,6 +3934,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, #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, \ @@ -4387,7 +4474,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } @@ -4974,7 +5061,6 @@ static int patch_alc880(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -5182,6 +5268,7 @@ static 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, @@ -5220,6 +5307,7 @@ static 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, @@ -6582,7 +6670,6 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc260_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -7247,29 +7334,18 @@ static struct snd_kcontrol_new alc882_chmode_mixer[] = { static 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_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)}, /* CLFE 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)}, /* Side 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)}, - /* mute analog input loopbacks */ - {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)}, - /* Front Pin: output 0 (0x0c) */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -7306,14 +7382,8 @@ static struct hda_verb alc882_base_init_verbs[] = { /* 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)}, - {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)}, /* ADC2: mute amp left and right */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -7357,26 +7427,17 @@ static struct hda_verb alc_hp15_unsol_verbs[] = { static 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_OUT_ZERO}, - {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_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(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)}, + {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_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {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_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* mute analog input loopbacks */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {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}, @@ -7410,17 +7471,11 @@ static struct hda_verb alc885_init_verbs[] = { /* Mixer elements: 0x18, , 0x1a, 0x1b */ /* Input mixer1 */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {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 */ @@ -7906,18 +7961,6 @@ static struct hda_verb alc883_auto_init_verbs[] = { {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) */ @@ -7942,16 +7985,9 @@ static struct hda_verb alc883_auto_init_verbs[] = { /* 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, (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))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* 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))}, - + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { } }; @@ -9528,7 +9564,7 @@ static struct alc_config_preset alc882_presets[] = { .setup = alc889_acer_aspire_8930g_setup, .init_hook = alc_automute_amp, #ifdef CONFIG_SND_HDA_POWER_SAVE - .power_hook = alc889_power_eapd, + .power_hook = alc_power_eapd, #endif }, [ALC888_ACER_ASPIRE_7730G] = { @@ -10201,7 +10237,6 @@ static int patch_alc882(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc882_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -10324,8 +10359,14 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, .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 struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { ALC262_HP_MASTER_SWITCH, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), @@ -10483,6 +10524,12 @@ static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol, .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 struct snd_kcontrol_new alc262_hippo_mixer[] = { @@ -10963,11 +11010,17 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { { .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 = alc262_fujitsu_master_sw_put, .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), }, + { + .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", 0x18, 0, HDA_INPUT), @@ -10998,6 +11051,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { { .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 = alc262_lenovo_3000_master_sw_put, @@ -11152,6 +11206,11 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { .get = alc_mux_enum_get, .put = alc262_ultra_mux_enum_put, }, + { + .iface = NID_MAPPING, + .name = "Capture Source", + .private_value = 0x15, + }, { } /* end */ }; @@ -12041,7 +12100,6 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc262_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -12170,6 +12228,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { { .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, @@ -12185,6 +12244,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { { .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, @@ -12202,6 +12262,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { { .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, @@ -13105,8 +13166,6 @@ static int patch_alc268(struct hda_codec *codec) if (board_config == ALC268_AUTO) spec->init_hook = alc268_auto_init; - codec->proc_widget_hook = print_realtek_coef; - return 0; } @@ -13126,6 +13185,15 @@ static hda_nid_t alc269_capsrc_nids[1] = { 0x23, }; +static hda_nid_t alc269vb_adc_nids[1] = { + /* ADC1 */ + 0x09, +}; + +static hda_nid_t alc269vb_capsrc_nids[1] = { + 0x22, +}; + /* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24), * not a mux! */ @@ -13155,6 +13223,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { { .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, @@ -13175,6 +13244,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { .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, @@ -13192,7 +13262,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { } }; -static struct snd_kcontrol_new alc269_eeepc_mixer[] = { +static 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), @@ -13200,16 +13270,47 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = { { } /* end */ }; +static 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 */ +}; + /* capture mixer elements */ -static struct snd_kcontrol_new alc269_epc_capture_mixer[] = { +static 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", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static 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", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static 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", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static 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", 0x18, 0, HDA_INPUT), { } /* end */ }; /* FSC amilo */ -#define alc269_fujitsu_mixer alc269_eeepc_mixer +#define alc269_fujitsu_mixer alc269_laptop_mixer static struct hda_verb alc269_quanta_fl1_verbs[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -13352,7 +13453,7 @@ static void alc269_lifebook_init_hook(struct hda_codec *codec) alc269_lifebook_mic_autoswitch(codec); } -static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { +static 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 }, @@ -13363,7 +13464,7 @@ static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { {} }; -static struct hda_verb alc269_eeepc_amic_init_verbs[] = { +static 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 }, @@ -13373,6 +13474,28 @@ static struct hda_verb alc269_eeepc_amic_init_verbs[] = { {} }; +static 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 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}, + {} +}; + /* toggle speaker-output according to the hp-jack state */ static void alc269_speaker_automute(struct hda_codec *codec) { @@ -13390,7 +13513,7 @@ static void alc269_speaker_automute(struct hda_codec *codec) } /* unsolicited event for HP jack sensing */ -static void alc269_eeepc_unsol_event(struct hda_codec *codec, +static void alc269_laptop_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { @@ -13403,7 +13526,7 @@ static void alc269_eeepc_unsol_event(struct hda_codec *codec, } } -static void alc269_eeepc_dmic_setup(struct hda_codec *codec) +static void alc269_laptop_dmic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->ext_mic.pin = 0x18; @@ -13413,7 +13536,17 @@ static void alc269_eeepc_dmic_setup(struct hda_codec *codec) spec->auto_mic = 1; } -static void alc269_eeepc_amic_setup(struct hda_codec *codec) +static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + 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 void alc269_laptop_amic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->ext_mic.pin = 0x18; @@ -13423,7 +13556,7 @@ static void alc269_eeepc_amic_setup(struct hda_codec *codec) spec->auto_mic = 1; } -static void alc269_eeepc_inithook(struct hda_codec *codec) +static void alc269_laptop_inithook(struct hda_codec *codec) { alc269_speaker_automute(codec); alc_mic_automute(codec); @@ -13436,22 +13569,10 @@ static struct hda_verb alc269_init_verbs[] = { /* * Unmute ADC0 and set the default input to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (PCBeep, 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)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* - * Set up output mixers (0x0c - 0x0e) + * Set up output mixers (0x02 - 0x03) */ /* set vol=0 to output mixers */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, @@ -13476,26 +13597,57 @@ static struct hda_verb alc269_init_verbs[] = { {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_MUTE}, - {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}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* 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}, - /* FIXME: use matrix-type input source selection */ + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static 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 */ - {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)}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set EAPD */ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, { } }; @@ -13543,6 +13695,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err; static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + hda_nid_t real_capsrc_nids; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, alc269_ignore); @@ -13564,11 +13717,20 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc269_init_verbs); + if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { + add_verb(spec, alc269vb_init_verbs); + real_capsrc_nids = alc269vb_capsrc_nids[0]; + alc_ssid_check(codec, 0x21, 0x1b, 0x14); + } else { + add_verb(spec, alc269_init_verbs); + real_capsrc_nids = alc269_capsrc_nids[0]; + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + } + spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; /* set default input source */ - snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0], + snd_hda_codec_write_cache(codec, real_capsrc_nids, 0, AC_VERB_SET_CONNECT_SEL, spec->input_mux->items[0].index); @@ -13579,8 +13741,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (!spec->cap_mixer && !spec->no_analog) set_capture_mixer(codec); - alc_ssid_check(codec, 0x15, 0x1b, 0x14); - return 1; } @@ -13606,8 +13766,8 @@ static void alc269_auto_init(struct hda_codec *codec) static const char *alc269_models[ALC269_MODEL_LAST] = { [ALC269_BASIC] = "basic", [ALC269_QUANTA_FL1] = "quanta", - [ALC269_ASUS_AMIC] = "asus-amic", - [ALC269_ASUS_DMIC] = "asus-dmic", + [ALC269_AMIC] = "laptop-amic", + [ALC269_DMIC] = "laptop-dmic", [ALC269_FUJITSU] = "fujitsu", [ALC269_LIFEBOOK] = "lifebook", [ALC269_AUTO] = "auto", @@ -13616,41 +13776,49 @@ static const char *alc269_models[ALC269_MODEL_LAST] = { static struct snd_pci_quirk alc269_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80JT", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_AMIC), + 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", ALC269_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_ASUS_DMIC), + ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_ASUS_DMIC), + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), {} @@ -13680,47 +13848,75 @@ static struct alc_config_preset alc269_presets[] = { .setup = alc269_quanta_fl1_setup, .init_hook = alc269_quanta_fl1_init_hook, }, - [ALC269_ASUS_AMIC] = { - .mixers = { alc269_eeepc_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + [ALC269_AMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_analog_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_amic_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 = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_amic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc269_laptop_inithook, }, - [ALC269_ASUS_DMIC] = { - .mixers = { alc269_eeepc_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + [ALC269_DMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_dmic_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 = alc269_laptop_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc269_laptop_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 = alc269_laptop_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc269_laptop_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 = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_dmic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc269_laptop_inithook, }, [ALC269_FUJITSU] = { .mixers = { alc269_fujitsu_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + .cap_mixer = alc269_laptop_digital_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_dmic_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 = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_dmic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc269_laptop_inithook, }, [ALC269_LIFEBOOK] = { .mixers = { alc269_lifebook_mixer }, @@ -13741,6 +13937,7 @@ static int patch_alc269(struct hda_codec *codec) struct alc_spec *spec; int board_config; int err; + int is_alc269vb = 0; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -13757,6 +13954,7 @@ static int patch_alc269(struct hda_codec *codec) alc_free(codec); return -ENOMEM; } + is_alc269vb = 1; } board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, @@ -13792,7 +13990,7 @@ static int patch_alc269(struct hda_codec *codec) if (board_config != ALC269_AUTO) setup_preset(codec, &alc269_presets[board_config]); - if (codec->subsystem_id == 0x17aa3bf8) { + 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 */ @@ -13805,9 +14003,16 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_playback = &alc269_pcm_digital_playback; spec->stream_digital_capture = &alc269_pcm_digital_capture; - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; + if (!is_alc269vb) { + 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->cap_mixer) set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); @@ -13821,7 +14026,6 @@ static int patch_alc269(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc269_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -14939,13 +15143,16 @@ static int patch_alc861(struct hda_codec *codec) spec->vmaster_nid = 0x03; codec->patch_ops = alc_patch_ops; - if (board_config == ALC861_AUTO) + if (board_config == ALC861_AUTO) { spec->init_hook = alc861_auto_init; #ifdef CONFIG_SND_HDA_POWER_SAVE + spec->power_hook = alc_power_eapd; +#endif + } +#ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc861_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -15925,7 +16132,6 @@ static int patch_alc861vd(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc861vd_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -16392,13 +16598,6 @@ static 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}, - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - - {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)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -16448,6 +16647,28 @@ static struct hda_verb alc662_init_verbs[] = { { } }; +static struct hda_verb alc663_init_verbs[] = { + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } +}; + +static struct hda_verb alc272_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } +}; + static 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}, @@ -16467,61 +16688,6 @@ static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { {} }; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb alc662_auto_init_verbs[] = { - /* - * Unmute ADC 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 - * 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 */ - {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}, - - /* 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 mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -/* additional verbs for ALC663 */ -static struct hda_verb alc663_auto_init_verbs[] = { - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } -}; - static 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}, @@ -17272,6 +17438,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { 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), @@ -17307,6 +17474,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { 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), @@ -17952,9 +18120,13 @@ static int alc662_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - add_verb(spec, alc662_auto_init_verbs); - if (codec->vendor_id == 0x10ec0663) - add_verb(spec, alc663_auto_init_verbs); + add_verb(spec, alc662_init_verbs); + if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || + codec->vendor_id == 0x10ec0665) + add_verb(spec, alc663_init_verbs); + + if (codec->vendor_id == 0x10ec0272) + add_verb(spec, alc272_init_verbs); err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -18046,11 +18218,20 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->cap_mixer) set_capture_mixer(codec); - if (codec->vendor_id == 0x10ec0662) + + switch (codec->vendor_id) { + case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - else + break; + case 0x10ec0272: + case 0x10ec0663: + case 0x10ec0665: set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - + break; + case 0x10ec0273: + set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + break; + } spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; @@ -18060,7 +18241,6 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc662_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -18101,6 +18281,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", .patch = patch_alc662 }, { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, + { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 43b436c5d01..f419ee8d75f 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -122,6 +122,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol, #define SI3054_KCONTROL(kname,reg,mask) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = kname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | reg, \ .info = si3054_switch_info, \ .get = si3054_switch_get, \ .put = si3054_switch_put, \ diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 799ba257090..834c5980fe5 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -568,6 +568,11 @@ static hda_nid_t stac92hd83xxx_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x1f, 0x20, }; +static hda_nid_t stac92hd88xxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0f, 0x11, 0x1f, 0x20, +}; + #define STAC92HD71BXX_NUM_PINS 13 static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, @@ -2688,7 +2693,7 @@ static struct snd_kcontrol_new * stac_control_new(struct sigmatel_spec *spec, struct snd_kcontrol_new *ktemp, const char *name, - hda_nid_t nid) + unsigned int subdev) { struct snd_kcontrol_new *knew; @@ -2704,8 +2709,7 @@ stac_control_new(struct sigmatel_spec *spec, spec->kctls.alloced--; return NULL; } - if (nid) - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + knew->subdevice = subdev; return knew; } @@ -2715,7 +2719,7 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec, unsigned long val) { struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, - get_amp_nid_(val)); + HDA_SUBDEV_AMP_FLAG); if (!knew) return -ENOMEM; knew->index = idx; @@ -2874,6 +2878,13 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); + /* 92HD88: trace back up the link of nids to find the DAC */ + while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0])) + != AC_WID_AUD_OUT)) { + nid = conn[0]; + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + } for (j = 0; j < conn_len; j++) { wcaps = get_wcaps(codec, conn[j]); wtype = get_wcaps_type(wcaps); @@ -4160,34 +4171,52 @@ static void stac92xx_power_down(struct hda_codec *codec) static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, int enable); +static inline int get_int_hint(struct hda_codec *codec, const char *key, + int *valp) +{ + const char *p; + p = snd_hda_get_hint(codec, key); + if (p) { + unsigned long val; + if (!strict_strtoul(p, 0, &val)) { + *valp = val; + return 1; + } + } + return 0; +} + /* override some hints from the hwdep entry */ static void stac_store_hints(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - const char *p; int val; val = snd_hda_get_bool_hint(codec, "hp_detect"); if (val >= 0) spec->hp_detect = val; - p = snd_hda_get_hint(codec, "gpio_mask"); - if (p) { - spec->gpio_mask = simple_strtoul(p, NULL, 0); + if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { spec->eapd_mask = spec->gpio_dir = spec->gpio_data = spec->gpio_mask; } - p = snd_hda_get_hint(codec, "gpio_dir"); - if (p) - spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask; - p = snd_hda_get_hint(codec, "gpio_data"); - if (p) - spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask; - p = snd_hda_get_hint(codec, "eapd_mask"); - if (p) - spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask; + if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) + spec->gpio_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) + spec->gpio_dir &= spec->gpio_mask; + if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) + spec->eapd_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) + spec->gpio_mute &= spec->gpio_mask; val = snd_hda_get_bool_hint(codec, "eapd_switch"); if (val >= 0) 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; + } } static int stac92xx_init(struct hda_codec *codec) @@ -4372,18 +4401,8 @@ static void stac92xx_free_kctls(struct hda_codec *codec) static void stac92xx_shutup(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i; - hda_nid_t nid; - /* reset each pin before powering down DAC/ADC to avoid click noise */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type == AC_WID_PIN) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } + snd_hda_shutup_pins(codec); if (spec->eapd_mask) stac_gpio_set(codec, spec->gpio_mask, @@ -5272,7 +5291,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) hda_nid_t conn[STAC92HD83_DAC_COUNT + 1]; int err; int num_dacs; - hda_nid_t nid; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -5311,7 +5329,18 @@ again: stac92hd83xxx_brd_tbl[spec->board_config]); switch (codec->vendor_id) { + case 0x111d7666: + case 0x111d7667: + case 0x111d7668: + case 0x111d7669: + spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids); + spec->pin_nids = stac92hd88xxx_pin_nids; + spec->mono_nid = 0; + spec->digbeep_nid = 0; + spec->num_pwrs = 0; + break; case 0x111d7604: + case 0x111d76d4: case 0x111d7605: case 0x111d76d5: if (spec->board_config == STAC_92HD83XXX_PWR_REF) @@ -5325,6 +5354,11 @@ again: if (spec->board_config == STAC_92HD83XXX_HP) spec->gpio_led = 0x01; + if (find_mute_led_gpio(codec)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { spec->gpio_mask |= spec->gpio_led; @@ -5352,24 +5386,21 @@ again: return err; } - switch (spec->board_config) { - case STAC_DELL_S14: - nid = 0xf; - break; - default: - nid = 0xe; - break; - } - - num_dacs = snd_hda_get_connections(codec, nid, + /* docking output support */ + num_dacs = snd_hda_get_connections(codec, 0xF, conn, STAC92HD83_DAC_COUNT + 1) - 1; - if (num_dacs < 0) - num_dacs = STAC92HD83_DAC_COUNT; - - /* set port X to select the last DAC - */ - snd_hda_codec_write_cache(codec, nid, 0, + /* skip non-DAC connections */ + while (num_dacs >= 0 && + (get_wcaps_type(get_wcaps(codec, conn[num_dacs])) + != AC_WID_AUD_OUT)) + num_dacs--; + /* set port E and F to select the last DAC */ + if (num_dacs >= 0) { + snd_hda_codec_write_cache(codec, 0xE, 0, AC_VERB_SET_CONNECT_SEL, num_dacs); + snd_hda_codec_write_cache(codec, 0xF, 0, + AC_VERB_SET_CONNECT_SEL, num_dacs); + } codec->proc_widget_hook = stac92hd_proc_hook; @@ -5431,6 +5462,54 @@ static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, return 0; } +/* HP dv7 bass switch - GPIO5 */ +#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info +static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); + return 0; +} + +static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int gpio_data; + + gpio_data = (spec->gpio_data & ~0x20) | + (ucontrol->value.integer.value[0] ? 0x20 : 0); + if (gpio_data == spec->gpio_data) + return 0; + spec->gpio_data = gpio_data; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + return 1; +} + +static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac_hp_bass_gpio_info, + .get = stac_hp_bass_gpio_get, + .put = stac_hp_bass_gpio_put, +}; + +static int stac_add_hp_bass_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl, + "Bass Speaker Playback Switch", 0)) + return -ENOMEM; + + spec->gpio_mask |= 0x20; + spec->gpio_dir |= 0x20; + spec->gpio_data |= 0x20; + return 0; +} + static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5674,6 +5753,15 @@ again: return err; } + /* enable bass on HP dv7 */ + if (spec->board_config == STAC_HP_DV5) { + unsigned int cap; + cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); + cap &= AC_GPIO_IO_COUNT; + if (cap >= 6) + stac_add_hp_bass_switch(codec); + } + codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; @@ -6172,8 +6260,13 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx}, { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx}, { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx}, { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b70e26ad263..9ddc37300f6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,6 +54,8 @@ #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) @@ -157,6 +159,19 @@ struct via_spec { #endif }; +static struct via_spec * via_new_spec(struct hda_codec *codec) +{ + struct via_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return NULL; + + codec->spec = spec; + spec->codec = codec; + return spec; +} + static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { u32 vendor_id = codec->vendor_id; @@ -443,11 +458,27 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } +static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, + 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 0; +} + static void via_free_kctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1088,24 +1119,9 @@ static int via_independent_hp_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; - hda_nid_t nid; + hda_nid_t nid = kcontrol->private_value; unsigned int pinsel; - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - break; - case VT2002P: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } /* use !! to translate conn sel 2 for VT1718S */ pinsel = !!snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, @@ -1127,29 +1143,24 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active) } } +static hda_nid_t side_mute_channel(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; + default: return 0; + } +} + static int update_side_mute_status(struct hda_codec *codec) { /* mute side channel */ struct via_spec *spec = codec->spec; unsigned int parm = spec->hp_independent_mode ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; - hda_nid_t sw3; - - switch (spec->codec_type) { - case VT1708: - sw3 = 0x1b; - break; - case VT1709_10CH: - sw3 = 0x29; - break; - case VT1708B_8CH: - case VT1708S: - sw3 = 0x27; - break; - default: - sw3 = 0; - break; - } + hda_nid_t sw3 = side_mute_channel(spec); if (sw3) snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -1162,28 +1173,11 @@ 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 = spec->autocfg.hp_pins[0]; + hda_nid_t nid = kcontrol->private_value; unsigned int pinsel = ucontrol->value.enumerated.item[0]; /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; - - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ - spec->multiout.num_dacs = 4; - break; - case VT2002P: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid @@ -1207,18 +1201,55 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new via_hp_mixer[] = { +static struct snd_kcontrol_new via_hp_mixer[2] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Independent HP", - .count = 1, .info = via_independent_hp_info, .get = via_independent_hp_get, .put = via_independent_hp_put, }, - { } /* end */ + { + .iface = NID_MAPPING, + .name = "Independent HP", + }, }; +static int via_hp_build(struct via_spec *spec) +{ + struct snd_kcontrol_new *knew; + hda_nid_t nid; + + knew = via_clone_control(spec, &via_hp_mixer[0]); + if (knew == NULL) + return -ENOMEM; + + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + break; + case VT2002P: + nid = 0x35; + break; + case VT1812: + nid = 0x3d; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + knew->private_value = nid; + + knew = via_clone_control(spec, &via_hp_mixer[1]); + if (knew == NULL) + return -ENOMEM; + knew->subdevice = side_mute_channel(spec); + + return 0; +} + static void notify_aa_path_ctls(struct hda_codec *codec) { int i; @@ -1376,7 +1407,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new via_smart51_mixer[] = { +static struct snd_kcontrol_new via_smart51_mixer[2] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Smart 5.1", @@ -1385,9 +1416,36 @@ static struct snd_kcontrol_new via_smart51_mixer[] = { .get = via_smart51_get, .put = via_smart51_put, }, - {} /* end */ + { + .iface = NID_MAPPING, + .name = "Smart 5.1", + } }; +static int via_smart51_build(struct via_spec *spec) +{ + struct snd_kcontrol_new *knew; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + hda_nid_t nid; + int i; + + knew = via_clone_control(spec, &via_smart51_mixer[0]); + if (knew == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + nid = spec->autocfg.input_pins[index[i]]; + if (nid) { + knew = via_clone_control(spec, &via_smart51_mixer[1]); + if (knew == NULL) + return -ENOMEM; + knew->subdevice = nid; + } + } + + return 0; +} + /* capture mixer elements */ static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -1819,8 +1877,9 @@ static struct hda_pcm_stream vt1708_pcm_digital_capture = { static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - int i; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new *knew; + int err, i; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -1845,6 +1904,27 @@ static int via_build_controls(struct hda_codec *codec) 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++) { + err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); + if (err < 0) + 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_jack_power_state(codec); analog_low_current_mode(codec, 1); @@ -2481,9 +2561,9 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -2554,12 +2634,10 @@ static int patch_vt1708(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708_parse_auto_config(codec); if (err < 0) { @@ -2597,7 +2675,6 @@ static int patch_vt1708(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif - spec->codec = codec; INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -3010,9 +3087,9 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -3032,12 +3109,10 @@ static int patch_vt1709_10ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -3126,12 +3201,10 @@ static int patch_vt1709_6ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -3581,9 +3654,9 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -3605,12 +3678,10 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) if (get_codec_type(codec) == VT1708BCE) return patch_vt1708S(codec); /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708B_parse_auto_config(codec); if (err < 0) { @@ -3657,12 +3728,10 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708B_parse_auto_config(codec); if (err < 0) { @@ -4071,9 +4140,9 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -4103,12 +4172,10 @@ static int patch_vt1708S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708S_parse_auto_config(codec); if (err < 0) { @@ -4443,7 +4510,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -4464,12 +4531,10 @@ static int patch_vt1702(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1702_parse_auto_config(codec); if (err < 0) { @@ -4865,9 +4930,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -4888,12 +4953,10 @@ static int patch_vt1718S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1718S_parse_auto_config(codec); if (err < 0) { @@ -5014,6 +5077,7 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Mic Capture Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, .count = 1, .info = vt1716s_dmic_info, .get = vt1716s_dmic_get, @@ -5361,9 +5425,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -5384,12 +5448,10 @@ static int patch_vt1716S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1716S_parse_auto_config(codec); if (err < 0) { @@ -5719,7 +5781,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -5741,12 +5803,10 @@ static int patch_vt2002P(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt2002P_parse_auto_config(codec); if (err < 0) { @@ -6070,7 +6130,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -6092,12 +6152,10 @@ static int patch_vt1812(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1812_parse_auto_config(codec); if (err < 0) { |