mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 06:31:52 +00:00
Merge commit alsa/devel into topic/misc
Conflicts: include/sound/version.h
This commit is contained in:
commit
a29fb94ff4
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -1,3 +1,3 @@
|
||||
/* include/version.h */
|
||||
#define CONFIG_SND_VERSION "1.0.22"
|
||||
#define CONFIG_SND_VERSION "1.0.22.1"
|
||||
#define CONFIG_SND_DATE ""
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
snd_pcm_uframes_t pos;
|
||||
#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)
|
||||
|
||||
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;
|
||||
#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;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1657,7 +1673,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)
|
||||
@ -1790,6 +1806,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;
|
||||
@ -1811,15 +1828,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;
|
||||
@ -1848,8 +1867,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;
|
||||
}
|
||||
|
||||
@ -2007,6 +2028,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;
|
||||
@ -2035,15 +2057,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;
|
||||
@ -2066,8 +2090,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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -931,6 +931,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 +986,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) {
|
||||
@ -1706,7 +1708,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
|
||||
@ -1721,19 +1723,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;
|
||||
@ -1742,10 +1750,40 @@ 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
|
||||
@ -1757,6 +1795,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
|
||||
@ -3476,6 +3515,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;
|
||||
@ -3496,6 +3537,32 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
|
||||
|
||||
/**
|
||||
* snd_hda_add_nids - assign nids to controls from the array
|
||||
* @codec: the HDA codec
|
||||
* @kctl: struct snd_kcontrol
|
||||
* @index: index to kctl
|
||||
* @nids: the array of hda_nid_t
|
||||
* @size: count of hda_nid_t items
|
||||
*
|
||||
* This helper function assigns NIDs in the given array to a control element.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
|
||||
unsigned int index, hda_nid_t *nids, unsigned int size)
|
||||
{
|
||||
int err;
|
||||
|
||||
for ( ; size > 0; size--, nids++) {
|
||||
err = snd_hda_add_nid(codec, kctl, index, *nids);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_add_nids);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
unsigned int power_state);
|
||||
|
@ -789,6 +789,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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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, \
|
||||
@ -342,6 +343,8 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
|
||||
const struct snd_pci_quirk *tbl);
|
||||
int snd_hda_add_new_ctls(struct hda_codec *codec,
|
||||
struct snd_kcontrol_new *knew);
|
||||
int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl,
|
||||
unsigned int index, hda_nid_t *nids, unsigned int size);
|
||||
|
||||
/*
|
||||
* unsolicited event handler
|
||||
@ -464,13 +467,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);
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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,28 @@ 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_nids(codec, kctl, i, spec->capsrc_nids,
|
||||
spec->input_mux->num_items);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -701,6 +722,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 +830,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,
|
||||
@ -1608,6 +1631,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,
|
||||
@ -2129,6 +2153,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,
|
||||
@ -2250,6 +2275,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,
|
||||
@ -2582,7 +2608,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;
|
||||
}
|
||||
@ -3736,6 +3762,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,
|
||||
@ -3764,6 +3791,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,
|
||||
@ -4105,6 +4133,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,
|
||||
|
@ -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)
|
||||
@ -760,6 +762,10 @@ static int build_input(struct hda_codec *codec)
|
||||
err = snd_hda_ctl_add(codec, 0, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_add_nids(codec, kctl, 0, spec->adc_nid,
|
||||
spec->num_inputs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spec->num_inputs > 1 && !spec->mic_detect) {
|
||||
|
@ -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,15 @@ 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_nids(codec, kctl, i, spec->adc_nids,
|
||||
spec->input_mux->num_items);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2187,6 +2187,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,
|
||||
|
@ -633,6 +633,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 +685,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 +740,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 +794,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, \
|
||||
@ -2443,6 +2447,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
|
||||
@ -2457,8 +2470,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]);
|
||||
@ -2499,8 +2515,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;
|
||||
}
|
||||
@ -2527,6 +2542,73 @@ 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++) {
|
||||
err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids,
|
||||
spec->input_mux->num_items);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3832,6 +3914,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, \
|
||||
@ -3841,6 +3924,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, \
|
||||
@ -4380,7 +4464,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;
|
||||
}
|
||||
@ -5131,6 +5215,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,
|
||||
@ -5169,6 +5254,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,
|
||||
@ -10248,8 +10334,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),
|
||||
@ -10407,6 +10499,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[] = {
|
||||
@ -10887,11 +10985,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),
|
||||
@ -10922,6 +11026,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,
|
||||
@ -11076,6 +11181,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 */
|
||||
};
|
||||
|
||||
@ -12094,6 +12204,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,
|
||||
@ -12109,6 +12220,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,
|
||||
@ -12126,6 +12238,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,
|
||||
@ -13078,6 +13191,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,
|
||||
@ -13098,6 +13212,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,
|
||||
|
@ -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, \
|
||||
|
@ -2688,7 +2688,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 +2704,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 +2714,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;
|
||||
|
@ -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,28 @@ 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_nids(codec, kctl, i, spec->mux_nids,
|
||||
spec->input_mux->num_items);
|
||||
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 +2562,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 +2635,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 +2676,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 +3088,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 +3110,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 +3202,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 +3655,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 +3679,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 +3729,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 +4141,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 +4173,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 +4511,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 +4532,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 +4931,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 +4954,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 +5078,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 +5426,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 +5449,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 +5782,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 +5804,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 +6131,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 +6153,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) {
|
||||
|
Loading…
Reference in New Issue
Block a user