mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-24 23:52:40 +00:00
Merge branch 'for-next' into for-linus
4.18-rc1 merge material. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
cdbc653a04
129 changed files with 8718 additions and 1444 deletions
|
@ -96,6 +96,8 @@ source "sound/x86/Kconfig"
|
|||
|
||||
source "sound/synth/Kconfig"
|
||||
|
||||
source "sound/xen/Kconfig"
|
||||
|
||||
endif # SND
|
||||
|
||||
endif # !UML
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
obj-$(CONFIG_SOUND) += soundcore.o
|
||||
obj-$(CONFIG_DMASOUND) += oss/dmasound/
|
||||
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
|
||||
firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/
|
||||
firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/
|
||||
obj-$(CONFIG_SND_AOA) += aoa/
|
||||
|
||||
# This one must be compilable even if sound is configured out
|
||||
|
|
|
@ -1001,7 +1001,7 @@ static int snd_compress_proc_init(struct snd_compr *compr)
|
|||
compr->card->proc_root);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -240,6 +240,15 @@ void snd_device_free_all(struct snd_card *card)
|
|||
|
||||
if (snd_BUG_ON(!card))
|
||||
return;
|
||||
list_for_each_entry_safe_reverse(dev, next, &card->devices, list) {
|
||||
/* exception: free ctl and lowlevel stuff later */
|
||||
if (dev->type == SNDRV_DEV_CONTROL ||
|
||||
dev->type == SNDRV_DEV_LOWLEVEL)
|
||||
continue;
|
||||
__snd_device_free(dev);
|
||||
}
|
||||
|
||||
/* free all */
|
||||
list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
|
||||
__snd_device_free(dev);
|
||||
}
|
||||
|
|
|
@ -454,7 +454,7 @@ static struct snd_info_entry *create_subdir(struct module *mod,
|
|||
entry = snd_info_create_module_entry(mod, name, NULL);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return NULL;
|
||||
|
@ -470,7 +470,7 @@ int __init snd_info_init(void)
|
|||
snd_proc_root = snd_info_create_entry("asound", NULL);
|
||||
if (!snd_proc_root)
|
||||
return -ENOMEM;
|
||||
snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
snd_proc_root->mode = S_IFDIR | 0555;
|
||||
snd_proc_root->p = proc_mkdir("asound", NULL);
|
||||
if (!snd_proc_root->p)
|
||||
goto error;
|
||||
|
@ -716,7 +716,7 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent)
|
|||
kfree(entry);
|
||||
return NULL;
|
||||
}
|
||||
entry->mode = S_IFREG | S_IRUGO;
|
||||
entry->mode = S_IFREG | 0444;
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
mutex_init(&entry->access);
|
||||
INIT_LIST_HEAD(&entry->children);
|
||||
|
|
|
@ -703,7 +703,7 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
|
||||
static DEVICE_ATTR(id, 0644, card_id_show_attr, card_id_store_attr);
|
||||
|
||||
static ssize_t
|
||||
card_number_show_attr(struct device *dev,
|
||||
|
@ -713,7 +713,7 @@ card_number_show_attr(struct device *dev,
|
|||
return scnprintf(buf, PAGE_SIZE, "%i\n", card->number);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL);
|
||||
static DEVICE_ATTR(number, 0444, card_number_show_attr, NULL);
|
||||
|
||||
static struct attribute *card_dev_attrs[] = {
|
||||
&dev_attr_id.attr,
|
||||
|
|
|
@ -1247,7 +1247,7 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
|
|||
if (! entry)
|
||||
return;
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = snd_mixer_oss_proc_read;
|
||||
entry->c.text.write = snd_mixer_oss_proc_write;
|
||||
entry->private_data = mixer;
|
||||
|
|
|
@ -3045,7 +3045,7 @@ static void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
|
|||
continue;
|
||||
if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = snd_pcm_oss_proc_read;
|
||||
entry->c.text.write = snd_pcm_oss_proc_write;
|
||||
entry->private_data = pstr;
|
||||
|
|
|
@ -530,7 +530,7 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
|
|||
pcm->card->proc_root);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
|
@ -552,7 +552,7 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
|
|||
if (entry) {
|
||||
entry->c.text.read = snd_pcm_xrun_debug_read;
|
||||
entry->c.text.write = snd_pcm_xrun_debug_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->private_data = pstr;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -590,7 +590,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
|
|||
substream->pstr->proc_root);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
|
@ -647,7 +647,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
|
|||
entry->private_data = substream;
|
||||
entry->c.text.read = NULL;
|
||||
entry->c.text.write = snd_pcm_xrun_injection_write;
|
||||
entry->mode = S_IFREG | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0200;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
|
@ -1087,7 +1087,7 @@ static ssize_t show_pcm_class(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%s\n", str);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
|
||||
static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL);
|
||||
static struct attribute *pcm_dev_attrs[] = {
|
||||
&dev_attr_pcm_class.attr,
|
||||
NULL
|
||||
|
|
|
@ -45,10 +45,7 @@ static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
|
|||
|
||||
if (get_user(frames, src))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_playback_rewind(substream, frames);
|
||||
else
|
||||
err = snd_pcm_capture_rewind(substream, frames);
|
||||
err = snd_pcm_rewind(substream, frames);
|
||||
if (put_user(err, src))
|
||||
return -EFAULT;
|
||||
return err < 0 ? err : 0;
|
||||
|
@ -62,10 +59,7 @@ static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
|
|||
|
||||
if (get_user(frames, src))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_playback_forward(substream, frames);
|
||||
else
|
||||
err = snd_pcm_capture_forward(substream, frames);
|
||||
err = snd_pcm_forward(substream, frames);
|
||||
if (put_user(err, src))
|
||||
return -EFAULT;
|
||||
return err < 0 ? err : 0;
|
||||
|
|
|
@ -191,10 +191,7 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
|
|||
{
|
||||
snd_pcm_uframes_t avail;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
avail = snd_pcm_playback_avail(runtime);
|
||||
else
|
||||
avail = snd_pcm_capture_avail(runtime);
|
||||
avail = snd_pcm_avail(substream);
|
||||
if (avail > runtime->avail_max)
|
||||
runtime->avail_max = avail;
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
|
@ -1856,10 +1853,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
|
|||
* This check must happen after been added to the waitqueue
|
||||
* and having current state be INTERRUPTIBLE.
|
||||
*/
|
||||
if (is_playback)
|
||||
avail = snd_pcm_playback_avail(runtime);
|
||||
else
|
||||
avail = snd_pcm_capture_avail(runtime);
|
||||
avail = snd_pcm_avail(substream);
|
||||
if (avail >= runtime->twake)
|
||||
break;
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
|
@ -2175,10 +2169,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
|
|||
runtime->twake = runtime->control->avail_min ? : 1;
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
||||
snd_pcm_update_hw_ptr(substream);
|
||||
if (is_playback)
|
||||
avail = snd_pcm_playback_avail(runtime);
|
||||
else
|
||||
avail = snd_pcm_capture_avail(runtime);
|
||||
avail = snd_pcm_avail(substream);
|
||||
while (size > 0) {
|
||||
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
||||
snd_pcm_uframes_t cont;
|
||||
|
|
|
@ -36,6 +36,24 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
|
|||
void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t new_hw_ptr);
|
||||
|
||||
static inline snd_pcm_uframes_t
|
||||
snd_pcm_avail(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_playback_avail(substream->runtime);
|
||||
else
|
||||
return snd_pcm_capture_avail(substream->runtime);
|
||||
}
|
||||
|
||||
static inline snd_pcm_uframes_t
|
||||
snd_pcm_hw_avail(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_playback_hw_avail(substream->runtime);
|
||||
else
|
||||
return snd_pcm_capture_hw_avail(substream->runtime);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_PCM_TIMER
|
||||
void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
|
||||
void snd_pcm_timer_init(struct snd_pcm_substream *substream);
|
||||
|
|
|
@ -201,7 +201,7 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
|
|||
if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
|
||||
entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
|
||||
entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->private_data = substream;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
|
|
@ -99,6 +99,57 @@ static inline void down_write_nonblock(struct rw_semaphore *lock)
|
|||
cond_resched();
|
||||
}
|
||||
|
||||
#define PCM_LOCK_DEFAULT 0
|
||||
#define PCM_LOCK_IRQ 1
|
||||
#define PCM_LOCK_IRQSAVE 2
|
||||
|
||||
static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substream,
|
||||
unsigned int mode)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
if (substream->pcm->nonatomic) {
|
||||
down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
|
||||
mutex_lock(&substream->self_group.mutex);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case PCM_LOCK_DEFAULT:
|
||||
read_lock(&snd_pcm_link_rwlock);
|
||||
break;
|
||||
case PCM_LOCK_IRQ:
|
||||
read_lock_irq(&snd_pcm_link_rwlock);
|
||||
break;
|
||||
case PCM_LOCK_IRQSAVE:
|
||||
read_lock_irqsave(&snd_pcm_link_rwlock, flags);
|
||||
break;
|
||||
}
|
||||
spin_lock(&substream->self_group.lock);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
|
||||
unsigned int mode, unsigned long flags)
|
||||
{
|
||||
if (substream->pcm->nonatomic) {
|
||||
mutex_unlock(&substream->self_group.mutex);
|
||||
up_read(&snd_pcm_link_rwsem);
|
||||
} else {
|
||||
spin_unlock(&substream->self_group.lock);
|
||||
|
||||
switch (mode) {
|
||||
case PCM_LOCK_DEFAULT:
|
||||
read_unlock(&snd_pcm_link_rwlock);
|
||||
break;
|
||||
case PCM_LOCK_IRQ:
|
||||
read_unlock_irq(&snd_pcm_link_rwlock);
|
||||
break;
|
||||
case PCM_LOCK_IRQSAVE:
|
||||
read_unlock_irqrestore(&snd_pcm_link_rwlock, flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_stream_lock - Lock the PCM stream
|
||||
* @substream: PCM substream
|
||||
|
@ -109,13 +160,7 @@ static inline void down_write_nonblock(struct rw_semaphore *lock)
|
|||
*/
|
||||
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->pcm->nonatomic) {
|
||||
down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
|
||||
mutex_lock(&substream->self_group.mutex);
|
||||
} else {
|
||||
read_lock(&snd_pcm_link_rwlock);
|
||||
spin_lock(&substream->self_group.lock);
|
||||
}
|
||||
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_DEFAULT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
|
||||
|
||||
|
@ -127,13 +172,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
|
|||
*/
|
||||
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->pcm->nonatomic) {
|
||||
mutex_unlock(&substream->self_group.mutex);
|
||||
up_read(&snd_pcm_link_rwsem);
|
||||
} else {
|
||||
spin_unlock(&substream->self_group.lock);
|
||||
read_unlock(&snd_pcm_link_rwlock);
|
||||
}
|
||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_DEFAULT, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
|
||||
|
||||
|
@ -147,9 +186,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
|
|||
*/
|
||||
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (!substream->pcm->nonatomic)
|
||||
local_irq_disable();
|
||||
snd_pcm_stream_lock(substream);
|
||||
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQ);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
|
||||
|
||||
|
@ -161,19 +198,13 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
|
|||
*/
|
||||
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_stream_unlock(substream);
|
||||
if (!substream->pcm->nonatomic)
|
||||
local_irq_enable();
|
||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQ, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
|
||||
|
||||
unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
if (!substream->pcm->nonatomic)
|
||||
local_irq_save(flags);
|
||||
snd_pcm_stream_lock(substream);
|
||||
return flags;
|
||||
return __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQSAVE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
|
||||
|
||||
|
@ -187,9 +218,7 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
|
|||
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
|
||||
unsigned long flags)
|
||||
{
|
||||
snd_pcm_stream_unlock(substream);
|
||||
if (!substream->pcm->nonatomic)
|
||||
local_irq_restore(flags);
|
||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQSAVE, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
|
||||
|
||||
|
@ -857,6 +886,18 @@ static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline snd_pcm_uframes_t
|
||||
snd_pcm_calc_delay(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_uframes_t delay;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
delay = snd_pcm_playback_hw_avail(substream->runtime);
|
||||
else
|
||||
delay = snd_pcm_capture_avail(substream->runtime);
|
||||
return delay + substream->runtime->delay;
|
||||
}
|
||||
|
||||
int snd_pcm_status(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_status *status)
|
||||
{
|
||||
|
@ -908,21 +949,9 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
|
|||
_tstamp_end:
|
||||
status->appl_ptr = runtime->control->appl_ptr;
|
||||
status->hw_ptr = runtime->status->hw_ptr;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
status->avail = snd_pcm_playback_avail(runtime);
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
|
||||
runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
status->delay = runtime->buffer_size - status->avail;
|
||||
status->delay += runtime->delay;
|
||||
} else
|
||||
status->delay = 0;
|
||||
} else {
|
||||
status->avail = snd_pcm_capture_avail(runtime);
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
||||
status->delay = status->avail + runtime->delay;
|
||||
else
|
||||
status->delay = 0;
|
||||
}
|
||||
status->avail = snd_pcm_avail(substream);
|
||||
status->delay = snd_pcm_running(substream) ?
|
||||
snd_pcm_calc_delay(substream) : 0;
|
||||
status->avail_max = runtime->avail_max;
|
||||
status->overrange = runtime->overrange;
|
||||
runtime->avail_max = 0;
|
||||
|
@ -2610,10 +2639,9 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
|
|||
return ret < 0 ? 0 : frames;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_sframes_t ret;
|
||||
|
||||
if (frames == 0)
|
||||
|
@ -2623,33 +2651,14 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
|
|||
ret = do_pcm_hwsync(substream);
|
||||
if (!ret)
|
||||
ret = rewind_appl_ptr(substream, frames,
|
||||
snd_pcm_playback_hw_avail(runtime));
|
||||
snd_pcm_hw_avail(substream));
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_sframes_t ret;
|
||||
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
ret = do_pcm_hwsync(substream);
|
||||
if (!ret)
|
||||
ret = rewind_appl_ptr(substream, frames,
|
||||
snd_pcm_capture_hw_avail(runtime));
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_sframes_t ret;
|
||||
|
||||
if (frames == 0)
|
||||
|
@ -2659,25 +2668,7 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
|
|||
ret = do_pcm_hwsync(substream);
|
||||
if (!ret)
|
||||
ret = forward_appl_ptr(substream, frames,
|
||||
snd_pcm_playback_avail(runtime));
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_sframes_t ret;
|
||||
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
ret = do_pcm_hwsync(substream);
|
||||
if (!ret)
|
||||
ret = forward_appl_ptr(substream, frames,
|
||||
snd_pcm_capture_avail(runtime));
|
||||
snd_pcm_avail(substream));
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
return ret;
|
||||
}
|
||||
|
@ -2695,19 +2686,13 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
|
|||
static int snd_pcm_delay(struct snd_pcm_substream *substream,
|
||||
snd_pcm_sframes_t *delay)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
snd_pcm_sframes_t n = 0;
|
||||
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
err = do_pcm_hwsync(substream);
|
||||
if (!err) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
n = snd_pcm_playback_hw_avail(runtime);
|
||||
else
|
||||
n = snd_pcm_capture_avail(runtime);
|
||||
n += runtime->delay;
|
||||
}
|
||||
if (!err)
|
||||
n = snd_pcm_calc_delay(substream);
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
if (!err)
|
||||
*delay = n;
|
||||
|
@ -2834,10 +2819,7 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
|
|||
return -EFAULT;
|
||||
if (put_user(0, _frames))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
result = snd_pcm_playback_rewind(substream, frames);
|
||||
else
|
||||
result = snd_pcm_capture_rewind(substream, frames);
|
||||
result = snd_pcm_rewind(substream, frames);
|
||||
__put_user(result, _frames);
|
||||
return result < 0 ? result : 0;
|
||||
}
|
||||
|
@ -2852,10 +2834,7 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
|
|||
return -EFAULT;
|
||||
if (put_user(0, _frames))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
result = snd_pcm_playback_forward(substream, frames);
|
||||
else
|
||||
result = snd_pcm_capture_forward(substream, frames);
|
||||
result = snd_pcm_forward(substream, frames);
|
||||
__put_user(result, _frames);
|
||||
return result < 0 ? result : 0;
|
||||
}
|
||||
|
@ -2998,7 +2977,7 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
|
|||
/* provided only for OSS; capture-only and no value returned */
|
||||
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
|
||||
return -EINVAL;
|
||||
result = snd_pcm_capture_forward(substream, *frames);
|
||||
result = snd_pcm_forward(substream, *frames);
|
||||
return result < 0 ? result : 0;
|
||||
}
|
||||
case SNDRV_PCM_IOCTL_HW_PARAMS:
|
||||
|
@ -3140,82 +3119,46 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
return result;
|
||||
}
|
||||
|
||||
static __poll_t snd_pcm_playback_poll(struct file *file, poll_table * wait)
|
||||
static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
__poll_t mask;
|
||||
__poll_t mask, ok;
|
||||
snd_pcm_uframes_t avail;
|
||||
|
||||
pcm_file = file->private_data;
|
||||
|
||||
substream = pcm_file->substream;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ok = EPOLLOUT | EPOLLWRNORM;
|
||||
else
|
||||
ok = EPOLLIN | EPOLLRDNORM;
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
return EPOLLOUT | EPOLLWRNORM | EPOLLERR;
|
||||
runtime = substream->runtime;
|
||||
return ok | EPOLLERR;
|
||||
|
||||
runtime = substream->runtime;
|
||||
poll_wait(file, &runtime->sleep, wait);
|
||||
|
||||
mask = 0;
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
avail = snd_pcm_playback_avail(runtime);
|
||||
avail = snd_pcm_avail(substream);
|
||||
switch (runtime->status->state) {
|
||||
case SNDRV_PCM_STATE_RUNNING:
|
||||
case SNDRV_PCM_STATE_PREPARED:
|
||||
case SNDRV_PCM_STATE_PAUSED:
|
||||
if (avail >= runtime->control->avail_min) {
|
||||
mask = EPOLLOUT | EPOLLWRNORM;
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
case SNDRV_PCM_STATE_DRAINING:
|
||||
mask = 0;
|
||||
break;
|
||||
default:
|
||||
mask = EPOLLOUT | EPOLLWRNORM | EPOLLERR;
|
||||
break;
|
||||
}
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static __poll_t snd_pcm_capture_poll(struct file *file, poll_table * wait)
|
||||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
__poll_t mask;
|
||||
snd_pcm_uframes_t avail;
|
||||
|
||||
pcm_file = file->private_data;
|
||||
|
||||
substream = pcm_file->substream;
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
return EPOLLIN | EPOLLRDNORM | EPOLLERR;
|
||||
runtime = substream->runtime;
|
||||
|
||||
poll_wait(file, &runtime->sleep, wait);
|
||||
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
avail = snd_pcm_capture_avail(runtime);
|
||||
switch (runtime->status->state) {
|
||||
case SNDRV_PCM_STATE_RUNNING:
|
||||
case SNDRV_PCM_STATE_PREPARED:
|
||||
case SNDRV_PCM_STATE_PAUSED:
|
||||
if (avail >= runtime->control->avail_min) {
|
||||
mask = EPOLLIN | EPOLLRDNORM;
|
||||
break;
|
||||
}
|
||||
mask = 0;
|
||||
if (avail >= runtime->control->avail_min)
|
||||
mask = ok;
|
||||
break;
|
||||
case SNDRV_PCM_STATE_DRAINING:
|
||||
if (avail > 0) {
|
||||
mask = EPOLLIN | EPOLLRDNORM;
|
||||
break;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
mask = ok;
|
||||
if (!avail)
|
||||
mask |= EPOLLERR;
|
||||
}
|
||||
/* Fall through */
|
||||
break;
|
||||
default:
|
||||
mask = EPOLLIN | EPOLLRDNORM | EPOLLERR;
|
||||
mask = ok | EPOLLERR;
|
||||
break;
|
||||
}
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
|
@ -3707,7 +3650,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
|
|||
.open = snd_pcm_playback_open,
|
||||
.release = snd_pcm_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_pcm_playback_poll,
|
||||
.poll = snd_pcm_poll,
|
||||
.unlocked_ioctl = snd_pcm_ioctl,
|
||||
.compat_ioctl = snd_pcm_ioctl_compat,
|
||||
.mmap = snd_pcm_mmap,
|
||||
|
@ -3721,7 +3664,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
|
|||
.open = snd_pcm_capture_open,
|
||||
.release = snd_pcm_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_pcm_capture_poll,
|
||||
.poll = snd_pcm_poll,
|
||||
.unlocked_ioctl = snd_pcm_ioctl,
|
||||
.compat_ioctl = snd_pcm_ioctl_compat,
|
||||
.mmap = snd_pcm_mmap,
|
||||
|
|
|
@ -669,7 +669,7 @@ int snd_seq_event_port_attach(int client,
|
|||
/* Set up the port */
|
||||
memset(&portinfo, 0, sizeof(portinfo));
|
||||
portinfo.addr.client = client;
|
||||
strlcpy(portinfo.name, portname ? portname : "Unamed port",
|
||||
strlcpy(portinfo.name, portname ? portname : "Unnamed port",
|
||||
sizeof(portinfo.name));
|
||||
|
||||
portinfo.capability = cap;
|
||||
|
|
|
@ -371,9 +371,7 @@ static int initialize_timer(struct snd_seq_timer *tmr)
|
|||
|
||||
tmr->ticks = 1;
|
||||
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
|
||||
unsigned long r = t->hw.resolution;
|
||||
if (! r && t->hw.c_resolution)
|
||||
r = t->hw.c_resolution(t);
|
||||
unsigned long r = snd_timer_resolution(tmr->timeri);
|
||||
if (r) {
|
||||
tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
|
||||
if (! tmr->ticks)
|
||||
|
|
|
@ -427,25 +427,35 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
}
|
||||
EXPORT_SYMBOL(snd_timer_close);
|
||||
|
||||
static unsigned long snd_timer_hw_resolution(struct snd_timer *timer)
|
||||
{
|
||||
if (timer->hw.c_resolution)
|
||||
return timer->hw.c_resolution(timer);
|
||||
else
|
||||
return timer->hw.resolution;
|
||||
}
|
||||
|
||||
unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
|
||||
{
|
||||
struct snd_timer * timer;
|
||||
unsigned long ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (timeri == NULL)
|
||||
return 0;
|
||||
timer = timeri->timer;
|
||||
if (timer) {
|
||||
if (timer->hw.c_resolution)
|
||||
return timer->hw.c_resolution(timer);
|
||||
return timer->hw.resolution;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
ret = snd_timer_hw_resolution(timer);
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_timer_resolution);
|
||||
|
||||
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
struct snd_timer *timer = ti->timer;
|
||||
unsigned long resolution = 0;
|
||||
struct snd_timer_instance *ts;
|
||||
struct timespec tstamp;
|
||||
|
@ -457,14 +467,14 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
|
|||
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
|
||||
event > SNDRV_TIMER_EVENT_PAUSE))
|
||||
return;
|
||||
if (event == SNDRV_TIMER_EVENT_START ||
|
||||
event == SNDRV_TIMER_EVENT_CONTINUE)
|
||||
resolution = snd_timer_resolution(ti);
|
||||
if (timer &&
|
||||
(event == SNDRV_TIMER_EVENT_START ||
|
||||
event == SNDRV_TIMER_EVENT_CONTINUE))
|
||||
resolution = snd_timer_hw_resolution(timer);
|
||||
if (ti->ccallback)
|
||||
ti->ccallback(ti, event, &tstamp, resolution);
|
||||
if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
|
||||
return;
|
||||
timer = ti->timer;
|
||||
if (timer == NULL)
|
||||
return;
|
||||
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
|
||||
|
@ -771,10 +781,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
spin_lock_irqsave(&timer->lock, flags);
|
||||
|
||||
/* remember the current resolution */
|
||||
if (timer->hw.c_resolution)
|
||||
resolution = timer->hw.c_resolution(timer);
|
||||
else
|
||||
resolution = timer->hw.resolution;
|
||||
resolution = snd_timer_hw_resolution(timer);
|
||||
|
||||
/* loop for all active instances
|
||||
* Here we cannot use list_for_each_entry because the active_list of a
|
||||
|
@ -1014,12 +1021,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
|
|||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (event == SNDRV_TIMER_EVENT_MSTART ||
|
||||
event == SNDRV_TIMER_EVENT_MCONTINUE ||
|
||||
event == SNDRV_TIMER_EVENT_MRESUME) {
|
||||
if (timer->hw.c_resolution)
|
||||
resolution = timer->hw.c_resolution(timer);
|
||||
else
|
||||
resolution = timer->hw.resolution;
|
||||
}
|
||||
event == SNDRV_TIMER_EVENT_MRESUME)
|
||||
resolution = snd_timer_hw_resolution(timer);
|
||||
list_for_each_entry(ti, &timer->active_list_head, active_list) {
|
||||
if (ti->ccallback)
|
||||
ti->ccallback(ti, event, tstamp, resolution);
|
||||
|
@ -1656,10 +1659,8 @@ static int snd_timer_user_gstatus(struct file *file,
|
|||
mutex_lock(®ister_mutex);
|
||||
t = snd_timer_find(&tid);
|
||||
if (t != NULL) {
|
||||
if (t->hw.c_resolution)
|
||||
gstatus.resolution = t->hw.c_resolution(t);
|
||||
else
|
||||
gstatus.resolution = t->hw.resolution;
|
||||
spin_lock_irq(&t->lock);
|
||||
gstatus.resolution = snd_timer_hw_resolution(t);
|
||||
if (t->hw.precise_resolution) {
|
||||
t->hw.precise_resolution(t, &gstatus.resolution_num,
|
||||
&gstatus.resolution_den);
|
||||
|
@ -1667,6 +1668,7 @@ static int snd_timer_user_gstatus(struct file *file,
|
|||
gstatus.resolution_num = gstatus.resolution;
|
||||
gstatus.resolution_den = 1000000000uL;
|
||||
}
|
||||
spin_unlock_irq(&t->lock);
|
||||
} else {
|
||||
err = -ENODEV;
|
||||
}
|
||||
|
|
|
@ -421,13 +421,15 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
|||
kctl->private_free = master_free;
|
||||
|
||||
/* additional (constant) TLV read */
|
||||
if (tlv &&
|
||||
(tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
|
||||
tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
|
||||
tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
memcpy(master->tlv, tlv, sizeof(master->tlv));
|
||||
kctl->tlv.p = master->tlv;
|
||||
if (tlv) {
|
||||
unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
|
||||
if (type == SNDRV_CTL_TLVT_DB_SCALE ||
|
||||
type == SNDRV_CTL_TLVT_DB_MINMAX ||
|
||||
type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
memcpy(master->tlv, tlv, sizeof(master->tlv));
|
||||
kctl->tlv.p = master->tlv;
|
||||
}
|
||||
}
|
||||
|
||||
return kctl;
|
||||
|
|
|
@ -768,20 +768,7 @@ static int loopback_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops loopback_playback_ops = {
|
||||
.open = loopback_open,
|
||||
.close = loopback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = loopback_hw_params,
|
||||
.hw_free = loopback_hw_free,
|
||||
.prepare = loopback_prepare,
|
||||
.trigger = loopback_trigger,
|
||||
.pointer = loopback_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_ops loopback_capture_ops = {
|
||||
static const struct snd_pcm_ops loopback_pcm_ops = {
|
||||
.open = loopback_open,
|
||||
.close = loopback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
|
@ -804,8 +791,8 @@ static int loopback_pcm_new(struct loopback *loopback,
|
|||
substreams, substreams, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops);
|
||||
|
||||
pcm->private_data = loopback;
|
||||
pcm->info_flags = 0;
|
||||
|
|
|
@ -1042,7 +1042,7 @@ static void dummy_proc_init(struct snd_dummy *chip)
|
|||
if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
|
||||
snd_info_set_text_ops(entry, chip, dummy_proc_read);
|
||||
entry->c.text.write = dummy_proc_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->private_data = chip;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,11 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
|||
static struct platform_device *platform_devices[SNDRV_CARDS];
|
||||
static int device_count;
|
||||
|
||||
module_param_array(index, int, NULL, S_IRUGO);
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
|
||||
module_param_array(id, charp, NULL, S_IRUGO);
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
|
||||
module_param_array(enable, bool, NULL, S_IRUGO);
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
|
||||
|
||||
MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>");
|
||||
|
|
|
@ -104,7 +104,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
|
|||
if (entry) {
|
||||
if (opl4->hardware < OPL3_HW_OPL4_ML) {
|
||||
/* OPL4 can access 4 MB external ROM/SRAM */
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->size = 4 * 1024 * 1024;
|
||||
} else {
|
||||
/* OPL4-ML has 1 MB internal ROM */
|
||||
|
|
|
@ -60,11 +60,11 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
|||
static struct platform_device *platform_devices[SNDRV_CARDS];
|
||||
static int device_count;
|
||||
|
||||
module_param_array(index, int, NULL, S_IRUGO);
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
|
||||
module_param_array(id, charp, NULL, S_IRUGO);
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
|
||||
module_param_array(enable, bool, NULL, S_IRUGO);
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
|
||||
|
||||
MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig");
|
||||
|
|
|
@ -183,7 +183,7 @@ void snd_bebob_proc_init(struct snd_bebob *bebob)
|
|||
bebob->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
|
||||
dice-pcm.o dice-hwdep.o dice.o
|
||||
dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
|
||||
dice-alesis.o dice-extension.o dice-mytek.o
|
||||
obj-$(CONFIG_SND_DICE) += snd-dice.o
|
||||
|
|
52
sound/firewire/dice/dice-alesis.c
Normal file
52
sound/firewire/dice/dice-alesis.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dice-alesis.c - a part of driver for DICE based devices
|
||||
*
|
||||
* Copyright (c) 2018 Takashi Sakamoto
|
||||
*/
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
static const unsigned int
|
||||
alesis_io14_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
|
||||
{6, 6, 4}, /* Tx0 = Analog + S/PDIF. */
|
||||
{8, 4, 0}, /* Tx1 = ADAT1. */
|
||||
};
|
||||
|
||||
static const unsigned int
|
||||
alesis_io26_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
|
||||
{10, 10, 8}, /* Tx0 = Analog + S/PDIF. */
|
||||
{16, 8, 0}, /* Tx1 = ADAT1 + ADAT2. */
|
||||
};
|
||||
|
||||
int snd_dice_detect_alesis_formats(struct snd_dice *dice)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
if (data == 4 || data == 6) {
|
||||
memcpy(dice->tx_pcm_chs, alesis_io14_tx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
|
||||
sizeof(unsigned int));
|
||||
} else {
|
||||
memcpy(dice->rx_pcm_chs, alesis_io26_tx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
|
||||
sizeof(unsigned int));
|
||||
}
|
||||
|
||||
for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
|
||||
dice->rx_pcm_chs[0][i] = 8;
|
||||
|
||||
dice->tx_midi_ports[0] = 1;
|
||||
dice->rx_midi_ports[0] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
172
sound/firewire/dice/dice-extension.c
Normal file
172
sound/firewire/dice/dice-extension.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dice-extension.c - a part of driver for DICE based devices
|
||||
*
|
||||
* Copyright (c) 2018 Takashi Sakamoto
|
||||
*/
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
/* For TCD2210/2220, TCAT defines extension of application protocol. */
|
||||
|
||||
#define DICE_EXT_APP_SPACE 0xffffe0200000uLL
|
||||
|
||||
#define DICE_EXT_APP_CAPS_OFFSET 0x00
|
||||
#define DICE_EXT_APP_CAPS_SIZE 0x04
|
||||
#define DICE_EXT_APP_CMD_OFFSET 0x08
|
||||
#define DICE_EXT_APP_CMD_SIZE 0x0c
|
||||
#define DICE_EXT_APP_MIXER_OFFSET 0x10
|
||||
#define DICE_EXT_APP_MIXER_SIZE 0x14
|
||||
#define DICE_EXT_APP_PEAK_OFFSET 0x18
|
||||
#define DICE_EXT_APP_PEAK_SIZE 0x1c
|
||||
#define DICE_EXT_APP_ROUTER_OFFSET 0x20
|
||||
#define DICE_EXT_APP_ROUTER_SIZE 0x24
|
||||
#define DICE_EXT_APP_STREAM_OFFSET 0x28
|
||||
#define DICE_EXT_APP_STREAM_SIZE 0x2c
|
||||
#define DICE_EXT_APP_CURRENT_OFFSET 0x30
|
||||
#define DICE_EXT_APP_CURRENT_SIZE 0x34
|
||||
#define DICE_EXT_APP_STANDALONE_OFFSET 0x38
|
||||
#define DICE_EXT_APP_STANDALONE_SIZE 0x3c
|
||||
#define DICE_EXT_APP_APPLICATION_OFFSET 0x40
|
||||
#define DICE_EXT_APP_APPLICATION_SIZE 0x44
|
||||
|
||||
#define EXT_APP_STREAM_TX_NUMBER 0x0000
|
||||
#define EXT_APP_STREAM_RX_NUMBER 0x0004
|
||||
#define EXT_APP_STREAM_ENTRIES 0x0008
|
||||
#define EXT_APP_STREAM_ENTRY_SIZE 0x010c
|
||||
#define EXT_APP_NUMBER_AUDIO 0x0000
|
||||
#define EXT_APP_NUMBER_MIDI 0x0004
|
||||
#define EXT_APP_NAMES 0x0008
|
||||
#define EXT_APP_NAMES_SIZE 256
|
||||
#define EXT_APP_AC3 0x0108
|
||||
|
||||
#define EXT_APP_CONFIG_LOW_ROUTER 0x0000
|
||||
#define EXT_APP_CONFIG_LOW_STREAM 0x1000
|
||||
#define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000
|
||||
#define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000
|
||||
#define EXT_APP_CONFIG_HIGH_ROUTER 0x4000
|
||||
#define EXT_APP_CONFIG_HIGH_STREAM 0x5000
|
||||
|
||||
static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
|
||||
u32 offset, void *buf, size_t len)
|
||||
{
|
||||
return snd_fw_transaction(dice->unit,
|
||||
len == 4 ? TCODE_READ_QUADLET_REQUEST :
|
||||
TCODE_READ_BLOCK_REQUEST,
|
||||
section_addr + offset, buf, len, 0);
|
||||
}
|
||||
|
||||
static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
|
||||
u32 base_offset, unsigned int stream_count,
|
||||
unsigned int mode,
|
||||
unsigned int pcm_channels[MAX_STREAMS][3],
|
||||
unsigned int midi_ports[MAX_STREAMS])
|
||||
{
|
||||
u32 entry_offset;
|
||||
__be32 reg[2];
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < stream_count; ++i) {
|
||||
entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
|
||||
err = read_transaction(dice, section_addr,
|
||||
entry_offset + EXT_APP_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels[i][mode] = be32_to_cpu(reg[0]);
|
||||
midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
|
||||
{
|
||||
u32 base_offset;
|
||||
__be32 reg[2];
|
||||
unsigned int stream_count;
|
||||
int mode;
|
||||
int err = 0;
|
||||
|
||||
for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
|
||||
unsigned int cap;
|
||||
|
||||
/*
|
||||
* Some models report stream formats at highest mode, however
|
||||
* they don't support the mode. Check clock capabilities.
|
||||
*/
|
||||
if (mode == 2) {
|
||||
cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
|
||||
} else if (mode == 1) {
|
||||
cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
|
||||
} else {
|
||||
cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
|
||||
CLOCK_CAP_RATE_48000;
|
||||
}
|
||||
if (!(cap & dice->clock_caps))
|
||||
continue;
|
||||
|
||||
base_offset = 0x2000 * mode + 0x1000;
|
||||
|
||||
err = read_transaction(dice, section_addr,
|
||||
base_offset + EXT_APP_STREAM_TX_NUMBER,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
base_offset += EXT_APP_STREAM_ENTRIES;
|
||||
stream_count = be32_to_cpu(reg[0]);
|
||||
err = read_stream_entries(dice, section_addr, base_offset,
|
||||
stream_count, mode,
|
||||
dice->tx_pcm_chs,
|
||||
dice->tx_midi_ports);
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
|
||||
stream_count = be32_to_cpu(reg[1]);
|
||||
err = read_stream_entries(dice, section_addr, base_offset,
|
||||
stream_count,
|
||||
mode, dice->rx_pcm_chs,
|
||||
dice->rx_midi_ports);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dice_detect_extension_formats(struct snd_dice *dice)
|
||||
{
|
||||
__be32 *pointers;
|
||||
unsigned int i;
|
||||
u64 section_addr;
|
||||
int err;
|
||||
|
||||
pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
|
||||
if (pointers == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
|
||||
DICE_EXT_APP_SPACE, pointers,
|
||||
9 * sizeof(__be32) * 2, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* Check two of them for offset have the same value or not. */
|
||||
for (i = 0; i < 9; ++i) {
|
||||
int j;
|
||||
|
||||
for (j = i + 1; j < 9; ++j) {
|
||||
if (pointers[i * 2] == pointers[j * 2])
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
|
||||
err = detect_stream_formats(dice, section_addr);
|
||||
end:
|
||||
kfree(pointers);
|
||||
return err;
|
||||
}
|
|
@ -174,14 +174,19 @@
|
|||
*/
|
||||
#define GLOBAL_SAMPLE_RATE 0x05c
|
||||
|
||||
/*
|
||||
* Some old firmware versions do not have the following global registers.
|
||||
* Windows drivers produced by TCAT lost backward compatibility in its
|
||||
* early release because they can handle firmware only which supports the
|
||||
* following registers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The version of the DICE driver specification that this device conforms to;
|
||||
* read-only.
|
||||
*/
|
||||
#define GLOBAL_VERSION 0x060
|
||||
|
||||
/* Some old firmware versions do not have the following global registers: */
|
||||
|
||||
/*
|
||||
* Supported sample rates and clock sources; read-only.
|
||||
*/
|
||||
|
|
|
@ -101,27 +101,18 @@ int snd_dice_create_midi(struct snd_dice *dice)
|
|||
.close = midi_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
};
|
||||
__be32 reg;
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct snd_rawmidi_str *str;
|
||||
unsigned int midi_in_ports, midi_out_ports;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Use the number of MIDI conformant data channel at current sampling
|
||||
* transfer frequency.
|
||||
*/
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
midi_in_ports = be32_to_cpu(reg);
|
||||
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
midi_out_ports = be32_to_cpu(reg);
|
||||
midi_in_ports = 0;
|
||||
midi_out_ports = 0;
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
midi_in_ports = max(midi_in_ports, dice->tx_midi_ports[i]);
|
||||
midi_out_ports = max(midi_out_ports, dice->rx_midi_ports[i]);
|
||||
}
|
||||
|
||||
if (midi_in_ports + midi_out_ports == 0)
|
||||
return 0;
|
||||
|
|
46
sound/firewire/dice/dice-mytek.c
Normal file
46
sound/firewire/dice/dice-mytek.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dice-mytek.c - a part of driver for DICE based devices
|
||||
*
|
||||
* Copyright (c) 2018 Melvin Vermeeren
|
||||
*/
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
struct dice_mytek_spec {
|
||||
unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
};
|
||||
|
||||
static const struct dice_mytek_spec stereo_192_dsd_dac = {
|
||||
/* AES, TOSLINK, SPDIF, ADAT inputs on device */
|
||||
.tx_pcm_chs = {{8, 8, 8}, {0, 0, 0} },
|
||||
/* PCM 44.1-192, native DSD64/DSD128 to device */
|
||||
.rx_pcm_chs = {{4, 4, 4}, {0, 0, 0} }
|
||||
};
|
||||
|
||||
/*
|
||||
* Mytek has a few other firewire-capable devices, though newer models appear
|
||||
* to lack the port more often than not. As I don't have access to any of them
|
||||
* they are missing here. An example is the Mytek 8x192 ADDA, which is DICE.
|
||||
*/
|
||||
|
||||
int snd_dice_detect_mytek_formats(struct snd_dice *dice)
|
||||
{
|
||||
int i;
|
||||
const struct dice_mytek_spec *dev;
|
||||
|
||||
dev = &stereo_192_dsd_dac;
|
||||
|
||||
memcpy(dice->tx_pcm_chs, dev->tx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
memcpy(dice->rx_pcm_chs, dev->rx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
dice->tx_midi_ports[i] = 0;
|
||||
dice->rx_midi_ports[i] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -9,43 +9,115 @@
|
|||
|
||||
#include "dice.h"
|
||||
|
||||
static int dice_rate_constraint(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_substream *substream = rule->private;
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int index = substream->pcm->device;
|
||||
|
||||
const struct snd_interval *c =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_interval *r =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval rates = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1
|
||||
};
|
||||
unsigned int *pcm_channels;
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, rate;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
|
||||
if (!snd_interval_test(c, pcm_channels[mode]))
|
||||
continue;
|
||||
|
||||
rates.min = min(rates.min, rate);
|
||||
rates.max = max(rates.max, rate);
|
||||
}
|
||||
|
||||
return snd_interval_refine(r, &rates);
|
||||
}
|
||||
|
||||
static int dice_channels_constraint(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_substream *substream = rule->private;
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int index = substream->pcm->device;
|
||||
|
||||
const struct snd_interval *r =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *c =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_interval channels = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1
|
||||
};
|
||||
unsigned int *pcm_channels;
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, rate;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
|
||||
if (!snd_interval_test(r, rate))
|
||||
continue;
|
||||
|
||||
channels.min = min(channels.min, pcm_channels[mode]);
|
||||
channels.max = max(channels.max, pcm_channels[mode]);
|
||||
}
|
||||
|
||||
return snd_interval_refine(c, &channels);
|
||||
}
|
||||
|
||||
static int limit_channels_and_rates(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
enum amdtp_stream_direction dir,
|
||||
unsigned int index, unsigned int size)
|
||||
unsigned int index)
|
||||
{
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
struct amdtp_stream *stream;
|
||||
unsigned int rate;
|
||||
__be32 reg;
|
||||
int err;
|
||||
unsigned int *pcm_channels;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Retrieve current Multi Bit Linear Audio data channel and limit to
|
||||
* it.
|
||||
*/
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
size * index + TX_NUMBER_AUDIO,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
stream = &dice->rx_stream[index];
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
size * index + RX_NUMBER_AUDIO,
|
||||
®, sizeof(reg));
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
pcm_channels = dice->tx_pcm_chs[index];
|
||||
else
|
||||
pcm_channels = dice->rx_pcm_chs[index];
|
||||
|
||||
hw->channels_min = UINT_MAX;
|
||||
hw->channels_max = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int rate, channels;
|
||||
|
||||
rate = snd_dice_rates[i];
|
||||
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
|
||||
continue;
|
||||
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
|
||||
|
||||
channels = pcm_channels[mode];
|
||||
if (channels == 0)
|
||||
continue;
|
||||
hw->channels_min = min(hw->channels_min, channels);
|
||||
hw->channels_max = max(hw->channels_max, channels);
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->channels_min = hw->channels_max = be32_to_cpu(reg);
|
||||
|
||||
/* Retrieve current sampling transfer frequency and limit to it. */
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->rates = snd_pcm_rate_to_rate_bit(rate);
|
||||
snd_pcm_limit_hw_rates(runtime);
|
||||
|
||||
return 0;
|
||||
|
@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice,
|
|||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_pcm_hardware *hw = &runtime->hw;
|
||||
unsigned int index = substream->pcm->device;
|
||||
enum amdtp_stream_direction dir;
|
||||
struct amdtp_stream *stream;
|
||||
__be32 reg[2];
|
||||
unsigned int count, size;
|
||||
int err;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
hw->formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
stream = &dice->tx_stream[substream->pcm->device];
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
|
||||
sizeof(reg));
|
||||
stream = &dice->tx_stream[index];
|
||||
} else {
|
||||
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
stream = &dice->rx_stream[substream->pcm->device];
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
|
||||
sizeof(reg));
|
||||
stream = &dice->rx_stream[index];
|
||||
}
|
||||
|
||||
err = limit_channels_and_rates(dice, substream->runtime, dir,
|
||||
index);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
|
||||
if (substream->pcm->device >= count)
|
||||
return -ENXIO;
|
||||
|
||||
size = be32_to_cpu(reg[1]) * 4;
|
||||
err = limit_channels_and_rates(dice, substream->runtime, dir,
|
||||
substream->pcm->device, size);
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
dice_rate_constraint, substream,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
dice_channels_constraint, substream,
|
||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice,
|
|||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int source;
|
||||
bool internal;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_lock_try(dice);
|
||||
|
@ -105,6 +177,43 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
err = snd_dice_transaction_get_clock_source(dice, &source);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
switch (source) {
|
||||
case CLOCK_SOURCE_AES1:
|
||||
case CLOCK_SOURCE_AES2:
|
||||
case CLOCK_SOURCE_AES3:
|
||||
case CLOCK_SOURCE_AES4:
|
||||
case CLOCK_SOURCE_AES_ANY:
|
||||
case CLOCK_SOURCE_ADAT:
|
||||
case CLOCK_SOURCE_TDIF:
|
||||
case CLOCK_SOURCE_WC:
|
||||
internal = false;
|
||||
break;
|
||||
default:
|
||||
internal = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* When source of clock is not internal or any PCM streams are running,
|
||||
* available sampling rate is limited at current sampling rate.
|
||||
*/
|
||||
if (!internal ||
|
||||
amdtp_stream_pcm_running(&dice->tx_stream[0]) ||
|
||||
amdtp_stream_pcm_running(&dice->tx_stream[1]) ||
|
||||
amdtp_stream_pcm_running(&dice->rx_stream[0]) ||
|
||||
amdtp_stream_pcm_running(&dice->rx_stream[1])) {
|
||||
unsigned int rate;
|
||||
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
substream->runtime->hw.rate_min = rate;
|
||||
substream->runtime->hw.rate_max = rate;
|
||||
}
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
end:
|
||||
return err;
|
||||
|
@ -318,37 +427,19 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
__be32 reg;
|
||||
struct snd_pcm *pcm;
|
||||
unsigned int i, max_capture, max_playback, capture, playback;
|
||||
unsigned int capture, playback;
|
||||
int i, j;
|
||||
int err;
|
||||
|
||||
/* Check whether PCM substreams are required. */
|
||||
if (dice->force_two_pcms) {
|
||||
max_capture = max_playback = 2;
|
||||
} else {
|
||||
max_capture = max_playback = 0;
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
|
||||
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
capture = playback = 0;
|
||||
if (i < max_capture)
|
||||
capture = 1;
|
||||
if (i < max_playback)
|
||||
playback = 1;
|
||||
if (capture == 0 && playback == 0)
|
||||
break;
|
||||
for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
|
||||
if (dice->tx_pcm_chs[i][j] > 0)
|
||||
capture = 1;
|
||||
if (dice->rx_pcm_chs[i][j] > 0)
|
||||
playback = 1;
|
||||
}
|
||||
|
||||
err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
|
||||
&pcm);
|
||||
|
|
|
@ -148,12 +148,12 @@ static void dice_proc_read(struct snd_info_entry *entry,
|
|||
>> CLOCK_RATE_SHIFT));
|
||||
snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
|
||||
snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
|
||||
snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
|
||||
(buf.global.version >> 24) & 0xff,
|
||||
(buf.global.version >> 16) & 0xff,
|
||||
(buf.global.version >> 8) & 0xff,
|
||||
(buf.global.version >> 0) & 0xff);
|
||||
if (quadlets >= 90) {
|
||||
snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
|
||||
(buf.global.version >> 24) & 0xff,
|
||||
(buf.global.version >> 16) & 0xff,
|
||||
(buf.global.version >> 8) & 0xff,
|
||||
(buf.global.version >> 0) & 0xff);
|
||||
snd_iprintf(buffer, " clock caps:");
|
||||
for (i = 0; i <= 6; ++i)
|
||||
if (buf.global.clock_caps & (1 << i))
|
||||
|
@ -243,10 +243,74 @@ static void dice_proc_read(struct snd_info_entry *entry,
|
|||
}
|
||||
}
|
||||
|
||||
void snd_dice_create_proc(struct snd_dice *dice)
|
||||
static void dice_proc_read_formation(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
static const char *const rate_labels[] = {
|
||||
[SND_DICE_RATE_MODE_LOW] = "low",
|
||||
[SND_DICE_RATE_MODE_MIDDLE] = "middle",
|
||||
[SND_DICE_RATE_MODE_HIGH] = "high",
|
||||
};
|
||||
struct snd_dice *dice = entry->private_data;
|
||||
int i, j;
|
||||
|
||||
snd_iprintf(buffer, "Output stream from unit:\n");
|
||||
for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
|
||||
snd_iprintf(buffer, "\t%s", rate_labels[i]);
|
||||
snd_iprintf(buffer, "\tMIDI\n");
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
snd_iprintf(buffer, "Tx %u:", i);
|
||||
for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
|
||||
snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);
|
||||
snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);
|
||||
}
|
||||
|
||||
snd_iprintf(buffer, "Input stream to unit:\n");
|
||||
for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
|
||||
snd_iprintf(buffer, "\t%s", rate_labels[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
snd_iprintf(buffer, "Rx %u:", i);
|
||||
for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
|
||||
snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);
|
||||
snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
|
||||
const char *name,
|
||||
void (*op)(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer))
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if (!snd_card_proc_new(dice->card, "dice", &entry))
|
||||
snd_info_set_text_ops(entry, dice, dice_proc_read);
|
||||
entry = snd_info_create_card_entry(dice->card, name, root);
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
snd_info_set_text_ops(entry, dice, op);
|
||||
if (snd_info_register(entry) < 0)
|
||||
snd_info_free_entry(entry);
|
||||
}
|
||||
|
||||
void snd_dice_create_proc(struct snd_dice *dice)
|
||||
{
|
||||
struct snd_info_entry *root;
|
||||
|
||||
/*
|
||||
* All nodes are automatically removed at snd_card_disconnect(),
|
||||
* by following to link list.
|
||||
*/
|
||||
root = snd_info_create_card_entry(dice->card, "firewire",
|
||||
dice->card->proc_root);
|
||||
if (!root)
|
||||
return;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
}
|
||||
|
||||
add_node(dice, root, "dice", dice_proc_read);
|
||||
add_node(dice, root, "formation", dice_proc_read_formation);
|
||||
}
|
||||
|
|
|
@ -30,13 +30,43 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
|
|||
[6] = 192000,
|
||||
};
|
||||
|
||||
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
enum snd_dice_rate_mode *mode)
|
||||
{
|
||||
/* Corresponding to each entry in snd_dice_rates. */
|
||||
static const enum snd_dice_rate_mode modes[] = {
|
||||
[0] = SND_DICE_RATE_MODE_LOW,
|
||||
[1] = SND_DICE_RATE_MODE_LOW,
|
||||
[2] = SND_DICE_RATE_MODE_LOW,
|
||||
[3] = SND_DICE_RATE_MODE_MIDDLE,
|
||||
[4] = SND_DICE_RATE_MODE_MIDDLE,
|
||||
[5] = SND_DICE_RATE_MODE_HIGH,
|
||||
[6] = SND_DICE_RATE_MODE_HIGH,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
|
||||
if (!(dice->clock_caps & BIT(i)))
|
||||
continue;
|
||||
if (snd_dice_rates[i] != rate)
|
||||
continue;
|
||||
|
||||
*mode = modes[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
|
||||
* to GLOBAL_STATUS. Especially, just after powering on, these are different.
|
||||
*/
|
||||
static int ensure_phase_lock(struct snd_dice *dice)
|
||||
static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
__be32 reg, nominal;
|
||||
u32 data;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
|
||||
|
@ -44,9 +74,21 @@ static int ensure_phase_lock(struct snd_dice *dice)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
data &= ~CLOCK_RATE_MASK;
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
|
||||
if (snd_dice_rates[i] == rate)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(snd_dice_rates))
|
||||
return -EINVAL;
|
||||
data |= i << CLOCK_RATE_SHIFT;
|
||||
|
||||
if (completion_done(&dice->clock_accepted))
|
||||
reinit_completion(&dice->clock_accepted);
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
|
@ -192,6 +234,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
unsigned int rate, struct reg_params *params)
|
||||
{
|
||||
__be32 reg[2];
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, pcm_chs, midi_ports;
|
||||
struct amdtp_stream *streams;
|
||||
struct fw_iso_resources *resources;
|
||||
|
@ -206,12 +249,23 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
resources = dice->rx_resources;
|
||||
}
|
||||
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < params->count; i++) {
|
||||
unsigned int pcm_cache;
|
||||
unsigned int midi_cache;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
pcm_cache = dice->tx_pcm_chs[i][mode];
|
||||
midi_cache = dice->tx_midi_ports[i];
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
params->size * i + TX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
} else {
|
||||
pcm_cache = dice->rx_pcm_chs[i][mode];
|
||||
midi_cache = dice->rx_midi_ports[i];
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
params->size * i + RX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
|
@ -221,6 +275,14 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
pcm_chs = be32_to_cpu(reg[0]);
|
||||
midi_ports = be32_to_cpu(reg[1]);
|
||||
|
||||
/* These are important for developer of this driver. */
|
||||
if (pcm_chs != pcm_cache || midi_ports != midi_cache) {
|
||||
dev_info(&dice->unit->device,
|
||||
"cache mismatch: pcm: %u:%u, midi: %u:%u\n",
|
||||
pcm_chs, pcm_cache, midi_ports, midi_cache);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -256,6 +318,68 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int start_duplex_streams(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Stop transmission. */
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
release_resources(dice);
|
||||
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to ensure phase lock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Likely to have changed stream formats. */
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Start both streams. */
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
release_resources(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* MEMO: After this function, there're two states of streams:
|
||||
* - None streams are running.
|
||||
|
@ -265,17 +389,13 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
|||
{
|
||||
unsigned int curr_rate;
|
||||
unsigned int i;
|
||||
struct reg_params tx_params, rx_params;
|
||||
bool need_to_start;
|
||||
enum snd_dice_rate_mode mode;
|
||||
int err;
|
||||
|
||||
if (dice->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Check sampling transmission frequency. */
|
||||
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
|
@ -285,72 +405,36 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
|||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate)
|
||||
return -EINVAL;
|
||||
goto restart;
|
||||
|
||||
/* Judge to need to restart streams. */
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if (i < tx_params.count) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
|
||||
!amdtp_stream_running(&dice->tx_stream[i]))
|
||||
break;
|
||||
}
|
||||
if (i < rx_params.count) {
|
||||
if (amdtp_streaming_error(&dice->rx_stream[i]) ||
|
||||
!amdtp_stream_running(&dice->rx_stream[i]))
|
||||
break;
|
||||
}
|
||||
/* Check error of packet streaming. */
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]))
|
||||
break;
|
||||
if (amdtp_streaming_error(&dice->rx_stream[i]))
|
||||
break;
|
||||
}
|
||||
need_to_start = (i < MAX_STREAMS);
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
|
||||
if (need_to_start) {
|
||||
/* Stop transmission. */
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
release_resources(dice);
|
||||
|
||||
err = ensure_phase_lock(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to ensure phase lock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Start both streams. */
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* Check required streams are running or not. */
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (dice->tx_pcm_chs[i][mode] > 0 &&
|
||||
!amdtp_stream_running(&dice->tx_stream[i]))
|
||||
break;
|
||||
if (dice->rx_pcm_chs[i][mode] > 0 &&
|
||||
!amdtp_stream_running(&dice->rx_stream[i]))
|
||||
break;
|
||||
}
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
|
||||
return err;
|
||||
error:
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
release_resources(dice);
|
||||
return err;
|
||||
return 0;
|
||||
restart:
|
||||
return start_duplex_streams(dice, rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -484,6 +568,69 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
|
|||
}
|
||||
}
|
||||
|
||||
int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
|
||||
{
|
||||
unsigned int rate;
|
||||
enum snd_dice_rate_mode mode;
|
||||
__be32 reg[2];
|
||||
struct reg_params tx_params, rx_params;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/* If extended protocol is available, detect detail spec. */
|
||||
err = snd_dice_detect_extension_formats(dice);
|
||||
if (err >= 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Available stream format is restricted at current mode of sampling
|
||||
* clock.
|
||||
*/
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Just after owning the unit (GLOBAL_OWNER), the unit can return
|
||||
* invalid stream formats. Selecting clock parameters have an effect
|
||||
* for the unit to refine it.
|
||||
*/
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < tx_params.count; ++i) {
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
tx_params.size * i + TX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
|
||||
dice->tx_midi_ports[i] = max_t(unsigned int,
|
||||
be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
|
||||
}
|
||||
for (i = 0; i < rx_params.count; ++i) {
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
rx_params.size * i + RX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
|
||||
dice->rx_midi_ports[i] = max_t(unsigned int,
|
||||
be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dice_lock_changed(struct snd_dice *dice)
|
||||
{
|
||||
dice->dev_lock_changed = true;
|
||||
|
|
104
sound/firewire/dice/dice-tcelectronic.c
Normal file
104
sound/firewire/dice/dice-tcelectronic.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dice-tc_electronic.c - a part of driver for DICE based devices
|
||||
*
|
||||
* Copyright (c) 2018 Takashi Sakamoto
|
||||
*/
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
struct dice_tc_spec {
|
||||
unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
bool has_midi;
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec desktop_konnekt6 = {
|
||||
.tx_pcm_chs = {{6, 6, 2}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{6, 6, 4}, {0, 0, 0} },
|
||||
.has_midi = false,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec impact_twin = {
|
||||
.tx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec konnekt_8 = {
|
||||
.tx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec konnekt_24d = {
|
||||
.tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec konnekt_live = {
|
||||
.tx_pcm_chs = {{16, 16, 16}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{16, 16, 16}, {0, 0, 0} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec studio_konnekt_48 = {
|
||||
.tx_pcm_chs = {{16, 16, 8}, {16, 16, 7} },
|
||||
.rx_pcm_chs = {{16, 16, 8}, {14, 14, 7} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
static const struct dice_tc_spec digital_konnekt_x32 = {
|
||||
.tx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
|
||||
.rx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
|
||||
.has_midi = false,
|
||||
};
|
||||
|
||||
int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice)
|
||||
{
|
||||
static const struct {
|
||||
u32 model_id;
|
||||
const struct dice_tc_spec *spec;
|
||||
} *entry, entries[] = {
|
||||
{0x00000020, &konnekt_24d},
|
||||
{0x00000021, &konnekt_8},
|
||||
{0x00000022, &studio_konnekt_48},
|
||||
{0x00000023, &konnekt_live},
|
||||
{0x00000024, &desktop_konnekt6},
|
||||
{0x00000027, &impact_twin},
|
||||
{0x00000030, &digital_konnekt_x32},
|
||||
};
|
||||
struct fw_csr_iterator it;
|
||||
int key, val, model_id;
|
||||
int i;
|
||||
|
||||
model_id = 0;
|
||||
fw_csr_iterator_init(&it, dice->unit->directory);
|
||||
while (fw_csr_iterator_next(&it, &key, &val)) {
|
||||
if (key == CSR_MODEL) {
|
||||
model_id = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
|
||||
entry = entries + i;
|
||||
if (entry->model_id == model_id)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(entries))
|
||||
return -ENODEV;
|
||||
|
||||
memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
|
||||
if (entry->spec->has_midi) {
|
||||
dice->tx_midi_ports[0] = 1;
|
||||
dice->rx_midi_ports[0] = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -265,7 +265,7 @@ int snd_dice_transaction_reinit(struct snd_dice *dice)
|
|||
static int get_subaddrs(struct snd_dice *dice)
|
||||
{
|
||||
static const int min_values[10] = {
|
||||
10, 0x64 / 4,
|
||||
10, 0x60 / 4,
|
||||
10, 0x18 / 4,
|
||||
10, 0x18 / 4,
|
||||
0, 0,
|
||||
|
@ -301,33 +301,40 @@ static int get_subaddrs(struct snd_dice *dice)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the implemented DICE driver specification major version
|
||||
* number matches.
|
||||
*/
|
||||
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DICE_PRIVATE_SPACE +
|
||||
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
|
||||
&version, sizeof(version), 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (be32_to_cpu(pointers[1]) > 0x18) {
|
||||
/*
|
||||
* Check that the implemented DICE driver specification major
|
||||
* version number matches.
|
||||
*/
|
||||
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DICE_PRIVATE_SPACE +
|
||||
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
|
||||
&version, sizeof(version), 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
|
||||
dev_err(&dice->unit->device,
|
||||
"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
if ((version & cpu_to_be32(0xff000000)) !=
|
||||
cpu_to_be32(0x01000000)) {
|
||||
dev_err(&dice->unit->device,
|
||||
"unknown DICE version: 0x%08x\n",
|
||||
be32_to_cpu(version));
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Set up later. */
|
||||
dice->clock_caps = 1;
|
||||
}
|
||||
|
||||
dice->global_offset = be32_to_cpu(pointers[0]) * 4;
|
||||
dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
|
||||
dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
|
||||
dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
|
||||
dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
|
||||
|
||||
/* Set up later. */
|
||||
if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
|
||||
dice->clock_caps = 1;
|
||||
/* Old firmware doesn't support these fields. */
|
||||
if (pointers[7])
|
||||
dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
|
||||
if (pointers[9])
|
||||
dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
|
||||
end:
|
||||
kfree(pointers);
|
||||
return err;
|
||||
|
|
|
@ -15,40 +15,15 @@ MODULE_LICENSE("GPL v2");
|
|||
#define OUI_LOUD 0x000ff2
|
||||
#define OUI_FOCUSRITE 0x00130e
|
||||
#define OUI_TCELECTRONIC 0x000166
|
||||
#define OUI_ALESIS 0x000595
|
||||
#define OUI_MAUDIO 0x000d6c
|
||||
#define OUI_MYTEK 0x001ee8
|
||||
|
||||
#define DICE_CATEGORY_ID 0x04
|
||||
#define WEISS_CATEGORY_ID 0x00
|
||||
#define LOUD_CATEGORY_ID 0x10
|
||||
|
||||
/*
|
||||
* Some models support several isochronous channels, while these streams are not
|
||||
* always available. In this case, add the model name to this list.
|
||||
*/
|
||||
static bool force_two_pcm_support(struct fw_unit *unit)
|
||||
{
|
||||
static const char *const models[] = {
|
||||
/* TC Electronic models. */
|
||||
"StudioKonnekt48",
|
||||
/* Focusrite models. */
|
||||
"SAFFIRE_PRO_40",
|
||||
"LIQUID_SAFFIRE_56",
|
||||
"SAFFIRE_PRO_40_1",
|
||||
};
|
||||
char model[32];
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
|
||||
if (err < 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
if (strcmp(models[i], model) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return i < ARRAY_SIZE(models);
|
||||
}
|
||||
#define MODEL_ALESIS_IO_BOTH 0x000001
|
||||
|
||||
static int check_dice_category(struct fw_unit *unit)
|
||||
{
|
||||
|
@ -75,11 +50,6 @@ static int check_dice_category(struct fw_unit *unit)
|
|||
}
|
||||
}
|
||||
|
||||
if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
|
||||
if (force_two_pcm_support(unit))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vendor == OUI_WEISS)
|
||||
category = WEISS_CATEGORY_ID;
|
||||
else if (vendor == OUI_LOUD)
|
||||
|
@ -186,9 +156,6 @@ static void do_registration(struct work_struct *work)
|
|||
if (err < 0)
|
||||
return;
|
||||
|
||||
if (force_two_pcm_support(dice->unit))
|
||||
dice->force_two_pcms = true;
|
||||
|
||||
err = snd_dice_transaction_init(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -199,6 +166,10 @@ static void do_registration(struct work_struct *work)
|
|||
|
||||
dice_card_strings(dice);
|
||||
|
||||
err = dice->detect_formats(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_stream_init_duplex(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -239,14 +210,17 @@ error:
|
|||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
static int dice_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dice *dice;
|
||||
int err;
|
||||
|
||||
err = check_dice_category(unit);
|
||||
if (err < 0)
|
||||
return -ENODEV;
|
||||
if (!entry->driver_data) {
|
||||
err = check_dice_category(unit);
|
||||
if (err < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL);
|
||||
|
@ -256,6 +230,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
|||
dice->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dice);
|
||||
|
||||
if (!entry->driver_data) {
|
||||
dice->detect_formats = snd_dice_stream_detect_current_formats;
|
||||
} else {
|
||||
dice->detect_formats =
|
||||
(snd_dice_detect_formats_t)entry->driver_data;
|
||||
}
|
||||
|
||||
spin_lock_init(&dice->lock);
|
||||
mutex_init(&dice->mutex);
|
||||
init_completion(&dice->clock_accepted);
|
||||
|
@ -313,17 +294,98 @@ static void dice_bus_reset(struct fw_unit *unit)
|
|||
#define DICE_INTERFACE 0x000001
|
||||
|
||||
static const struct ieee1394_device_id dice_id_table[] = {
|
||||
/* M-Audio Profire 2626 has a different value in version field. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_MAUDIO,
|
||||
.model_id = 0x000010,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
|
||||
},
|
||||
/* M-Audio Profire 610 has a different value in version field. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_MAUDIO,
|
||||
.model_id = 0x000011,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
|
||||
},
|
||||
/* TC Electronic Konnekt 24D. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000020,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Konnekt 8. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000021,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Studio Konnekt 48. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000022,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Konnekt Live. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000023,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Desktop Konnekt 6. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000024,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Impact Twin. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000027,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* TC Electronic Digital Konnekt x32. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_TCELECTRONIC,
|
||||
.model_id = 0x000030,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
|
||||
},
|
||||
/* Alesis iO14/iO26. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_ALESIS,
|
||||
.model_id = MODEL_ALESIS_IO_BOTH,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats,
|
||||
},
|
||||
/* Mytek Stereo 192 DSD-DAC. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_MYTEK,
|
||||
.model_id = 0x000002,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats,
|
||||
},
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VERSION,
|
||||
.version = DICE_INTERFACE,
|
||||
},
|
||||
/* M-Audio Profire 610/2626 has a different value in version field. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID,
|
||||
.vendor_id = 0x000d6c,
|
||||
.specifier_id = 0x000d6c,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
|
||||
|
|
|
@ -63,6 +63,16 @@
|
|||
*/
|
||||
#define MAX_STREAMS 2
|
||||
|
||||
enum snd_dice_rate_mode {
|
||||
SND_DICE_RATE_MODE_LOW = 0,
|
||||
SND_DICE_RATE_MODE_MIDDLE,
|
||||
SND_DICE_RATE_MODE_HIGH,
|
||||
SND_DICE_RATE_MODE_COUNT,
|
||||
};
|
||||
|
||||
struct snd_dice;
|
||||
typedef int (*snd_dice_detect_formats_t)(struct snd_dice *dice);
|
||||
|
||||
struct snd_dice {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
|
@ -80,6 +90,11 @@ struct snd_dice {
|
|||
unsigned int rsrv_offset;
|
||||
|
||||
unsigned int clock_caps;
|
||||
unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int tx_midi_ports[MAX_STREAMS];
|
||||
unsigned int rx_midi_ports[MAX_STREAMS];
|
||||
snd_dice_detect_formats_t detect_formats;
|
||||
|
||||
struct fw_address_handler notification_handler;
|
||||
int owner_generation;
|
||||
|
@ -98,8 +113,6 @@ struct snd_dice {
|
|||
bool global_enabled;
|
||||
struct completion clock_accepted;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
bool force_two_pcms;
|
||||
};
|
||||
|
||||
enum snd_dice_addr_type {
|
||||
|
@ -190,11 +203,14 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
|
|||
#define SND_DICE_RATES_COUNT 7
|
||||
extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
|
||||
|
||||
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
enum snd_dice_rate_mode *mode);
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
|
||||
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_init_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_update_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
|
||||
|
||||
int snd_dice_stream_lock_try(struct snd_dice *dice);
|
||||
void snd_dice_stream_lock_release(struct snd_dice *dice);
|
||||
|
@ -207,4 +223,9 @@ void snd_dice_create_proc(struct snd_dice *dice);
|
|||
|
||||
int snd_dice_create_midi(struct snd_dice *dice);
|
||||
|
||||
int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_alesis_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_extension_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_mytek_formats(struct snd_dice *dice);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -79,7 +79,7 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
|
|||
if (root == NULL)
|
||||
return;
|
||||
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -52,7 +52,7 @@ void snd_ff_proc_init(struct snd_ff *ff)
|
|||
ff->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -219,7 +219,7 @@ void snd_efw_proc_init(struct snd_efw *efw)
|
|||
efw->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -569,18 +569,20 @@ static int isight_create_mixer(struct isight *isight)
|
|||
return err;
|
||||
isight->gain_max = be32_to_cpu(value);
|
||||
|
||||
isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
|
||||
isight->gain_tlv[1] = 2 * sizeof(unsigned int);
|
||||
isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX;
|
||||
isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
|
||||
|
||||
err = reg_read(isight, REG_GAIN_DB_START, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
|
||||
isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] =
|
||||
(s32)be32_to_cpu(value) * 100;
|
||||
|
||||
err = reg_read(isight, REG_GAIN_DB_END, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
|
||||
isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =
|
||||
(s32)be32_to_cpu(value) * 100;
|
||||
|
||||
ctl = snd_ctl_new1(&gain_control, isight);
|
||||
if (ctl)
|
||||
|
|
|
@ -107,7 +107,7 @@ void snd_motu_proc_init(struct snd_motu *motu)
|
|||
motu->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -103,7 +103,7 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
|
|||
oxfw->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -49,7 +49,6 @@ static bool detect_loud_models(struct fw_unit *unit)
|
|||
"Tapco LINK.firewire 4x6",
|
||||
"U.420"};
|
||||
char model[32];
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = fw_csr_string(unit->directory, CSR_MODEL,
|
||||
|
@ -57,12 +56,7 @@ static bool detect_loud_models(struct fw_unit *unit)
|
|||
if (err < 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
if (strcmp(models[i], model) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return (i < ARRAY_SIZE(models));
|
||||
return match_string(models, ARRAY_SIZE(models), model) >= 0;
|
||||
}
|
||||
|
||||
static int name_card(struct snd_oxfw *oxfw)
|
||||
|
|
|
@ -78,7 +78,7 @@ void snd_tscm_proc_init(struct snd_tscm *tscm)
|
|||
tscm->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
root->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
|
|
|
@ -65,10 +65,10 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
|
|||
{
|
||||
struct hdac_device *codec = dev_to_hdac_dev(dev);
|
||||
unsigned int verb = get_verb(reg);
|
||||
const unsigned int *v;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->vendor_verbs.used; i++) {
|
||||
unsigned int *v = snd_array_elem(&codec->vendor_verbs, i);
|
||||
snd_array_for_each(&codec->vendor_verbs, i, v) {
|
||||
if (verb == *v)
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ static int snd_cmi8328_mixer(struct snd_wss *chip)
|
|||
}
|
||||
|
||||
/* find index of an item in "-1"-ended array */
|
||||
int array_find(int array[], int item)
|
||||
static int array_find(int array[], int item)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -203,7 +203,7 @@ int array_find(int array[], int item)
|
|||
return -1;
|
||||
}
|
||||
/* the same for long */
|
||||
int array_find_l(long array[], long item)
|
||||
static int array_find_l(long array[], long item)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
|
@ -757,9 +757,9 @@ static int snd_msnd_pinnacle_cfg_reset(int cfg)
|
|||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
||||
|
||||
module_param_array(index, int, NULL, S_IRUGO);
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
|
||||
module_param_array(id, charp, NULL, S_IRUGO);
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
|
||||
|
||||
static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
||||
|
@ -801,22 +801,22 @@ MODULE_LICENSE("GPL");
|
|||
MODULE_FIRMWARE(INITCODEFILE);
|
||||
MODULE_FIRMWARE(PERMCODEFILE);
|
||||
|
||||
module_param_hw_array(io, long, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(io, long, ioport, NULL, 0444);
|
||||
MODULE_PARM_DESC(io, "IO port #");
|
||||
module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
|
||||
module_param_hw_array(mem, long, iomem, NULL, S_IRUGO);
|
||||
module_param_array(write_ndelay, int, NULL, S_IRUGO);
|
||||
module_param(calibrate_signal, int, S_IRUGO);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0444);
|
||||
module_param_hw_array(mem, long, iomem, NULL, 0444);
|
||||
module_param_array(write_ndelay, int, NULL, 0444);
|
||||
module_param(calibrate_signal, int, 0444);
|
||||
#ifndef MSND_CLASSIC
|
||||
module_param_array(digital, int, NULL, S_IRUGO);
|
||||
module_param_hw_array(cfg, long, ioport, NULL, S_IRUGO);
|
||||
module_param_array(reset, int, 0, S_IRUGO);
|
||||
module_param_hw_array(mpu_io, long, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(mpu_irq, int, irq, NULL, S_IRUGO);
|
||||
module_param_hw_array(ide_io0, long, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(ide_io1, long, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(ide_irq, int, irq, NULL, S_IRUGO);
|
||||
module_param_hw_array(joystick_io, long, ioport, NULL, S_IRUGO);
|
||||
module_param_array(digital, int, NULL, 0444);
|
||||
module_param_hw_array(cfg, long, ioport, NULL, 0444);
|
||||
module_param_array(reset, int, 0, 0444);
|
||||
module_param_hw_array(mpu_io, long, ioport, NULL, 0444);
|
||||
module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
|
||||
module_param_hw_array(ide_io0, long, ioport, NULL, 0444);
|
||||
module_param_hw_array(ide_io1, long, ioport, NULL, 0444);
|
||||
module_param_hw_array(ide_irq, int, irq, NULL, 0444);
|
||||
module_param_hw_array(joystick_io, long, ioport, NULL, 0444);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -592,7 +592,7 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
*vport = devm_ioport_map(devptr, port[dev], 0x10);
|
||||
if (*vport == NULL) {
|
||||
snd_printk(KERN_ERR PFX
|
||||
"I/O port cannot be iomaped.\n");
|
||||
"I/O port cannot be iomapped.\n");
|
||||
err = -EBUSY;
|
||||
goto err_unmap1;
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
|
||||
if (!vmss_port) {
|
||||
snd_printk(KERN_ERR PFX
|
||||
"MSS port I/O cannot be iomaped.\n");
|
||||
"MSS port I/O cannot be iomapped.\n");
|
||||
err = -EBUSY;
|
||||
goto err_unmap2;
|
||||
}
|
||||
|
|
|
@ -448,7 +448,7 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
|
|||
if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
|
||||
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read);
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->c.text.write = snd_ac97_proc_regs_write;
|
||||
#endif
|
||||
if (snd_info_register(entry) < 0) {
|
||||
|
@ -474,7 +474,7 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
|
|||
|
||||
sprintf(name, "codec97#%d", bus->num);
|
||||
if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
|
|
|
@ -258,7 +258,7 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
|
|||
|
||||
while (!(ad1889_readw(chip, AD_AC97_ACIC) & AD_AC97_ACIC_ACRDY)
|
||||
&& --retry)
|
||||
mdelay(1);
|
||||
usleep_range(1000, 2000);
|
||||
if (!retry) {
|
||||
dev_err(chip->card->dev, "[%s] Link is not ready.\n",
|
||||
__func__);
|
||||
|
@ -872,7 +872,7 @@ snd_ad1889_init(struct snd_ad1889 *chip)
|
|||
ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
|
||||
ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
|
||||
|
||||
mdelay(10);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
/* enable Master and Target abort interrupts */
|
||||
ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
|
||||
|
|
|
@ -69,27 +69,27 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
|||
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
||||
static bool enable_hpi_hwdep = 1;
|
||||
|
||||
module_param_array(index, int, NULL, S_IRUGO);
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
|
||||
|
||||
module_param_array(id, charp, NULL, S_IRUGO);
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
|
||||
|
||||
module_param_array(enable, bool, NULL, S_IRUGO);
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
|
||||
|
||||
module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
|
||||
module_param(enable_hpi_hwdep, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_hpi_hwdep,
|
||||
"ALSA enable HPI hwdep for AudioScience soundcard ");
|
||||
|
||||
/* identify driver */
|
||||
#ifdef KERNEL_ALSA_BUILD
|
||||
static char *build_info = "Built using headers from kernel source";
|
||||
module_param(build_info, charp, S_IRUGO);
|
||||
module_param(build_info, charp, 0444);
|
||||
MODULE_PARM_DESC(build_info, "Built using headers from kernel source");
|
||||
#else
|
||||
static char *build_info = "Built within ALSA source";
|
||||
module_param(build_info, charp, S_IRUGO);
|
||||
module_param(build_info, charp, 0444);
|
||||
MODULE_PARM_DESC(build_info, "Built within ALSA source");
|
||||
#endif
|
||||
|
||||
|
|
|
@ -46,14 +46,14 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin");
|
|||
#endif
|
||||
|
||||
static int prealloc_stream_buf;
|
||||
module_param(prealloc_stream_buf, int, S_IRUGO);
|
||||
module_param(prealloc_stream_buf, int, 0444);
|
||||
MODULE_PARM_DESC(prealloc_stream_buf,
|
||||
"Preallocate size for per-adapter stream buffer");
|
||||
|
||||
/* Allow the debug level to be changed after module load.
|
||||
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
|
||||
*/
|
||||
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
|
||||
module_param(hpi_debug_level, int, 0644);
|
||||
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
|
||||
|
||||
/* List of adapters found */
|
||||
|
|
|
@ -431,7 +431,7 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu)
|
|||
if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
|
||||
entry->c.text.write = snd_ca0106_proc_reg_write32;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
|
||||
|
@ -440,12 +440,12 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu)
|
|||
if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
|
||||
entry->c.text.write = snd_ca0106_proc_reg_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
|
||||
entry->c.text.write = snd_ca0106_proc_i2c_write;
|
||||
entry->private_data = emu;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
|
||||
|
|
|
@ -1139,7 +1139,7 @@ static int save_mixer_state(struct cmipci *cm)
|
|||
struct snd_ctl_elem_value *val;
|
||||
unsigned int i;
|
||||
|
||||
val = kmalloc(sizeof(*val), GFP_ATOMIC);
|
||||
val = kmalloc(sizeof(*val), GFP_KERNEL);
|
||||
if (!val)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < CM_SAVED_MIXERS; i++) {
|
||||
|
|
|
@ -58,7 +58,7 @@ MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
|
|||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
|
||||
module_param_array(external_amp, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
|
||||
MODULE_PARM_DESC(external_amp, "Force to enable external amplifier.");
|
||||
module_param_array(thinkpad, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
|
||||
module_param_array(mmap_valid, bool, NULL, 0444);
|
||||
|
|
|
@ -2849,7 +2849,7 @@ static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
|
|||
entry->private_data = chip;
|
||||
entry->c.ops = &snd_cs46xx_proc_io_ops;
|
||||
entry->size = region->size;
|
||||
entry->mode = S_IFREG | S_IRUSR;
|
||||
entry->mode = S_IFREG | 0400;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
|
|
|
@ -798,7 +798,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
|
||||
if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
entry->mode = S_IFDIR | 0555;
|
||||
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -814,7 +814,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -826,7 +826,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_modules_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -838,7 +838,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -850,7 +850,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -862,7 +862,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -874,7 +874,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
|
|||
if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = chip;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = cs46xx_dsp_proc_scb_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
|
|
@ -271,7 +271,7 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
|
|||
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = scb_info;
|
||||
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
|
||||
entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
static bool use_system_timer;
|
||||
MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
|
||||
module_param(use_system_timer, bool, S_IRUGO);
|
||||
module_param(use_system_timer, bool, 0444);
|
||||
|
||||
struct ct_timer_ops {
|
||||
void (*init)(struct ct_timer_instance *);
|
||||
|
|
|
@ -26,9 +26,9 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
|
|||
static unsigned int reference_rate = 48000;
|
||||
static unsigned int multiple = 2;
|
||||
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
|
||||
module_param(reference_rate, uint, S_IRUGO);
|
||||
module_param(reference_rate, uint, 0444);
|
||||
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
|
||||
module_param(multiple, uint, S_IRUGO);
|
||||
module_param(multiple, uint, 0444);
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
|
|
|
@ -59,7 +59,7 @@ static int get_firmware(const struct firmware **fw_entry,
|
|||
dev_dbg(chip->card->dev,
|
||||
"firmware requested: %s\n", card_fw[fw_index].data);
|
||||
snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
|
||||
err = request_firmware(fw_entry, name, pci_device(chip));
|
||||
err = request_firmware(fw_entry, name, &chip->pci->dev);
|
||||
if (err < 0)
|
||||
dev_err(chip->card->dev,
|
||||
"get_firmware(): Firmware not available (%d)\n", err);
|
||||
|
|
|
@ -559,10 +559,4 @@ static inline int monitor_index(const struct echoaudio *chip, int out, int in)
|
|||
return out * num_busses_in(chip) + in;
|
||||
}
|
||||
|
||||
|
||||
#ifndef pci_device
|
||||
#define pci_device(chip) (&chip->pci->dev)
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _ECHOAUDIO_H_ */
|
||||
|
|
|
@ -1070,7 +1070,7 @@ static int snd_emu10k1x_proc_init(struct emu10k1x *emu)
|
|||
if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
|
||||
entry->c.text.write = snd_emu10k1x_proc_reg_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->private_data = emu;
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ static char *audigy_outs[32] = {
|
|||
/* 0x0f */ "Rear Right",
|
||||
/* 0x10 */ "AC97 Front Left",
|
||||
/* 0x11 */ "AC97 Front Right",
|
||||
/* 0x12 */ "ADC Caputre Left",
|
||||
/* 0x12 */ "ADC Capture Left",
|
||||
/* 0x13 */ "ADC Capture Right",
|
||||
/* 0x14 */ NULL,
|
||||
/* 0x15 */ NULL,
|
||||
|
@ -421,14 +421,10 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
|
|||
snd_fx8010_irq_handler_t *handler,
|
||||
unsigned char gpr_running,
|
||||
void *private_data,
|
||||
struct snd_emu10k1_fx8010_irq **r_irq)
|
||||
struct snd_emu10k1_fx8010_irq *irq)
|
||||
{
|
||||
struct snd_emu10k1_fx8010_irq *irq;
|
||||
unsigned long flags;
|
||||
|
||||
irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
|
||||
if (irq == NULL)
|
||||
return -ENOMEM;
|
||||
irq->handler = handler;
|
||||
irq->gpr_running = gpr_running;
|
||||
irq->private_data = private_data;
|
||||
|
@ -443,8 +439,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
|
|||
emu->fx8010.irq_handlers = irq;
|
||||
}
|
||||
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
|
||||
if (r_irq)
|
||||
*r_irq = irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -468,7 +462,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
|
|||
tmp->next = tmp->next->next;
|
||||
}
|
||||
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
|
||||
kfree(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1724,7 +1724,7 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
|
|||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
|
||||
snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq);
|
||||
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
|
||||
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
|
||||
pcm->tram_shift = 0;
|
||||
|
|
|
@ -135,7 +135,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
|
|||
/* 15 */ "Rear Right",
|
||||
/* 16 */ "AC97 Front Left",
|
||||
/* 17 */ "AC97 Front Right",
|
||||
/* 18 */ "ADC Caputre Left",
|
||||
/* 18 */ "ADC Capture Left",
|
||||
/* 19 */ "ADC Capture Right",
|
||||
/* 20 */ "???",
|
||||
/* 21 */ "???",
|
||||
|
@ -574,32 +574,32 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
|
|||
if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
|
||||
entry->c.text.write = snd_emu_proc_io_reg_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -621,35 +621,35 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
|
|||
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
|
||||
entry->c.text.read = snd_emu10k1_proc_acode_read;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -248,13 +248,13 @@ __found_pages:
|
|||
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
|
||||
{
|
||||
if (addr & ~emu->dma_mask) {
|
||||
dev_err(emu->card->dev,
|
||||
dev_err_ratelimited(emu->card->dev,
|
||||
"max memory size is 0x%lx (addr = 0x%lx)!!\n",
|
||||
emu->dma_mask, (unsigned long)addr);
|
||||
return 0;
|
||||
}
|
||||
if (addr & (EMUPAGESIZE-1)) {
|
||||
dev_err(emu->card->dev, "page is not aligned\n");
|
||||
dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
@ -345,7 +345,7 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
|
|||
else
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
|
||||
if (! is_valid_page(emu, addr)) {
|
||||
dev_err(emu->card->dev,
|
||||
dev_err_ratelimited(emu->card->dev,
|
||||
"emu: failure page = %d\n", idx);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
|
|
|
@ -127,11 +127,15 @@ comment "Set to Y if you want auto-loading the codec driver"
|
|||
|
||||
config SND_HDA_CODEC_HDMI
|
||||
tristate "Build HDMI/DisplayPort HD-audio codec support"
|
||||
select SND_DYNAMIC_MINORS
|
||||
help
|
||||
Say Y or M here to include HDMI and DisplayPort HD-audio codec
|
||||
support in snd-hda-intel driver. This includes all AMD/ATI,
|
||||
Intel and Nvidia HDMI/DisplayPort codecs.
|
||||
|
||||
Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS
|
||||
to assure the multiple streams for DP-MST support.
|
||||
|
||||
comment "Set to Y if you want auto-loading the codec driver"
|
||||
depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
|
||||
|
||||
|
|
|
@ -793,11 +793,11 @@ EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
|
|||
*/
|
||||
void snd_hda_apply_verbs(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_verb **v;
|
||||
int i;
|
||||
for (i = 0; i < codec->verbs.used; i++) {
|
||||
struct hda_verb **v = snd_array_elem(&codec->verbs, i);
|
||||
|
||||
snd_array_for_each(&codec->verbs, i, v)
|
||||
snd_hda_sequence_write(codec, *v);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
|
||||
|
||||
|
@ -890,10 +890,10 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
|
|||
static bool pin_config_match(struct hda_codec *codec,
|
||||
const struct hda_pintbl *pins)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->init_pins.used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
hda_nid_t nid = pin->nid;
|
||||
u32 cfg = pin->cfg;
|
||||
const struct hda_pintbl *t_pins;
|
||||
|
|
|
@ -481,9 +481,10 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
|
|||
struct snd_array *array,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
struct hda_pincfg *pin;
|
||||
int i;
|
||||
for (i = 0; i < array->used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(array, i);
|
||||
|
||||
snd_array_for_each(array, i, pin) {
|
||||
if (pin->nid == nid)
|
||||
return pin;
|
||||
}
|
||||
|
@ -618,14 +619,15 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
|
|||
*/
|
||||
void snd_hda_shutup_pins(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
|
||||
/* don't shut up pins when unloading the driver; otherwise it breaks
|
||||
* the default pin setup at the next load of the driver
|
||||
*/
|
||||
if (codec->bus->shutdown)
|
||||
return;
|
||||
for (i = 0; i < codec->init_pins.used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
/* use read here for syncing after issuing each verb */
|
||||
snd_hda_codec_read(codec, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
|
||||
|
@ -638,13 +640,14 @@ EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
|
|||
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
|
||||
static void restore_shutup_pins(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
|
||||
if (!codec->pins_shutup)
|
||||
return;
|
||||
if (codec->bus->shutdown)
|
||||
return;
|
||||
for (i = 0; i < codec->init_pins.used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
snd_hda_codec_write(codec, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pin->ctrl);
|
||||
|
@ -697,8 +700,7 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
|
|||
struct hda_cvt_setup *p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->cvt_setups.used; i++) {
|
||||
p = snd_array_elem(&codec->cvt_setups, i);
|
||||
snd_array_for_each(&codec->cvt_setups, i, p) {
|
||||
if (p->nid == nid)
|
||||
return p;
|
||||
}
|
||||
|
@ -1076,8 +1078,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
/* make other inactive cvts with the same stream-tag dirty */
|
||||
type = get_wcaps_type(get_wcaps(codec, nid));
|
||||
list_for_each_codec(c, codec->bus) {
|
||||
for (i = 0; i < c->cvt_setups.used; i++) {
|
||||
p = snd_array_elem(&c->cvt_setups, i);
|
||||
snd_array_for_each(&c->cvt_setups, i, p) {
|
||||
if (!p->active && p->stream_tag == stream_tag &&
|
||||
get_wcaps_type(get_wcaps(c, p->nid)) == type)
|
||||
p->dirty = 1;
|
||||
|
@ -1140,12 +1141,11 @@ static void really_cleanup_stream(struct hda_codec *codec,
|
|||
static void purify_inactive_streams(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_codec *c;
|
||||
struct hda_cvt_setup *p;
|
||||
int i;
|
||||
|
||||
list_for_each_codec(c, codec->bus) {
|
||||
for (i = 0; i < c->cvt_setups.used; i++) {
|
||||
struct hda_cvt_setup *p;
|
||||
p = snd_array_elem(&c->cvt_setups, i);
|
||||
snd_array_for_each(&c->cvt_setups, i, p) {
|
||||
if (p->dirty)
|
||||
really_cleanup_stream(c, p);
|
||||
}
|
||||
|
@ -1156,10 +1156,10 @@ static void purify_inactive_streams(struct hda_codec *codec)
|
|||
/* clean up all streams; called from suspend */
|
||||
static void hda_cleanup_all_streams(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_cvt_setup *p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->cvt_setups.used; i++) {
|
||||
struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
|
||||
snd_array_for_each(&codec->cvt_setups, i, p) {
|
||||
if (p->stream_tag)
|
||||
really_cleanup_stream(codec, p);
|
||||
}
|
||||
|
@ -1493,10 +1493,10 @@ static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
|
|||
val1 = ((int)val1) * ((int)val2);
|
||||
if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
|
||||
val2 |= TLV_DB_SCALE_MUTE;
|
||||
tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
|
||||
tlv[1] = 2 * sizeof(unsigned int);
|
||||
tlv[2] = val1;
|
||||
tlv[3] = val2;
|
||||
tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
|
||||
tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
|
||||
tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
|
||||
tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1544,10 +1544,10 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|||
nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
|
||||
step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
|
||||
step = (step + 1) * 25;
|
||||
tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
|
||||
tlv[1] = 2 * sizeof(unsigned int);
|
||||
tlv[2] = -nums * step;
|
||||
tlv[3] = step;
|
||||
tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
|
||||
tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
|
||||
tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
|
||||
tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
|
||||
|
||||
|
@ -1845,10 +1845,10 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
|
|||
} else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
|
||||
tlv = kctl->tlv.p;
|
||||
|
||||
if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE)
|
||||
if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
|
||||
return 0;
|
||||
|
||||
step = tlv[3];
|
||||
step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
|
||||
step &= ~TLV_DB_SCALE_MUTE;
|
||||
if (!step)
|
||||
return 0;
|
||||
|
@ -1860,7 +1860,7 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
|
|||
}
|
||||
|
||||
arg->step = step;
|
||||
val = -tlv[2] / step;
|
||||
val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
|
||||
if (val > 0) {
|
||||
put_kctl_with_value(slave, val);
|
||||
return val;
|
||||
|
@ -2175,6 +2175,8 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|||
int idx = kcontrol->private_value;
|
||||
struct hda_spdif_out *spdif;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return -EINVAL;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
ucontrol->value.iec958.status[0] = spdif->status & 0xff;
|
||||
|
@ -2282,6 +2284,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|||
unsigned short val;
|
||||
int change;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return -EINVAL;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
nid = spdif->nid;
|
||||
|
@ -2308,6 +2312,8 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
|
|||
int idx = kcontrol->private_value;
|
||||
struct hda_spdif_out *spdif;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return -EINVAL;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
|
||||
|
@ -2336,6 +2342,8 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
|
|||
unsigned short val;
|
||||
int change;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return -EINVAL;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
nid = spdif->nid;
|
||||
|
@ -2461,10 +2469,10 @@ EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
|
|||
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
struct hda_spdif_out *spdif;
|
||||
int i;
|
||||
for (i = 0; i < codec->spdif_out.used; i++) {
|
||||
struct hda_spdif_out *spdif =
|
||||
snd_array_elem(&codec->spdif_out, i);
|
||||
|
||||
snd_array_for_each(&codec->spdif_out, i, spdif) {
|
||||
if (spdif->nid == nid)
|
||||
return spdif;
|
||||
}
|
||||
|
@ -2483,6 +2491,8 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
|
|||
{
|
||||
struct hda_spdif_out *spdif;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
spdif->nid = (u16)-1;
|
||||
|
@ -2503,6 +2513,8 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
|
|||
struct hda_spdif_out *spdif;
|
||||
unsigned short val;
|
||||
|
||||
if (WARN_ON(codec->spdif_out.used <= idx))
|
||||
return;
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
if (spdif->nid != nid) {
|
||||
|
|
|
@ -748,8 +748,10 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
|
|||
return err;
|
||||
strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (apcm == NULL)
|
||||
if (apcm == NULL) {
|
||||
snd_device_free(chip->card, pcm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
apcm->chip = chip;
|
||||
apcm->pcm = pcm;
|
||||
apcm->codec = codec;
|
||||
|
|
|
@ -264,10 +264,10 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
|
|||
int anchor_nid)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
struct nid_path *path;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->paths.used; i++) {
|
||||
struct nid_path *path = snd_array_elem(&spec->paths, i);
|
||||
snd_array_for_each(&spec->paths, i, path) {
|
||||
if (path->depth <= 0)
|
||||
continue;
|
||||
if ((!from_nid || path->path[0] == from_nid) &&
|
||||
|
@ -325,10 +325,10 @@ EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
|
|||
static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const struct nid_path *path;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->paths.used; i++) {
|
||||
struct nid_path *path = snd_array_elem(&spec->paths, i);
|
||||
snd_array_for_each(&spec->paths, i, path) {
|
||||
if (path->path[0] == nid)
|
||||
return true;
|
||||
}
|
||||
|
@ -351,11 +351,11 @@ static bool is_reachable_path(struct hda_codec *codec,
|
|||
static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const struct nid_path *path;
|
||||
int i;
|
||||
|
||||
val &= AMP_VAL_COMPARE_MASK;
|
||||
for (i = 0; i < spec->paths.used; i++) {
|
||||
struct nid_path *path = snd_array_elem(&spec->paths, i);
|
||||
snd_array_for_each(&spec->paths, i, path) {
|
||||
if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
|
||||
return true;
|
||||
}
|
||||
|
@ -638,13 +638,13 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
|
|||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
int type = get_wcaps_type(get_wcaps(codec, nid));
|
||||
const struct nid_path *path;
|
||||
int i, n;
|
||||
|
||||
if (nid == codec->core.afg)
|
||||
return true;
|
||||
|
||||
for (n = 0; n < spec->paths.used; n++) {
|
||||
struct nid_path *path = snd_array_elem(&spec->paths, n);
|
||||
snd_array_for_each(&spec->paths, n, path) {
|
||||
if (!path->active)
|
||||
continue;
|
||||
if (codec->power_save_node) {
|
||||
|
@ -2065,7 +2065,7 @@ static int parse_output_paths(struct hda_codec *codec)
|
|||
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
|
||||
HDA_OUTPUT, spec->vmaster_tlv);
|
||||
if (spec->dac_min_mute)
|
||||
spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
|
||||
spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2696,10 +2696,10 @@ static const struct snd_kcontrol_new out_jack_mode_enum = {
|
|||
static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const struct snd_kcontrol_new *kctl;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->kctls.used; i++) {
|
||||
struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
|
||||
snd_array_for_each(&spec->kctls, i, kctl) {
|
||||
if (!strcmp(kctl->name, name) && kctl->index == idx)
|
||||
return true;
|
||||
}
|
||||
|
@ -4021,8 +4021,7 @@ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
|
|||
struct nid_path *path;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < spec->paths.used; n++) {
|
||||
path = snd_array_elem(&spec->paths, n);
|
||||
snd_array_for_each(&spec->paths, n, path) {
|
||||
if (!path->depth)
|
||||
continue;
|
||||
if (path->path[0] == nid ||
|
||||
|
@ -5831,10 +5830,10 @@ static void init_digital(struct hda_codec *codec)
|
|||
*/
|
||||
static void clear_unsol_on_unused_pins(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->init_pins.used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
hda_nid_t nid = pin->nid;
|
||||
if (is_jack_detectable(codec, nid) &&
|
||||
!snd_hda_jack_tbl_get(codec, nid))
|
||||
|
|
|
@ -2209,7 +2209,18 @@ static struct snd_pci_quirk power_save_blacklist[] = {
|
|||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
|
||||
SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
|
||||
SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
|
||||
SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */
|
||||
SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
|
||||
/* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */
|
||||
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0),
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */
|
||||
SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */
|
||||
SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0),
|
||||
/* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
|
||||
SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
|
||||
|
|
|
@ -80,10 +80,10 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
|
|||
struct snd_array *list,
|
||||
char *buf)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < list->used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(list, i);
|
||||
snd_array_for_each(list, i, pin) {
|
||||
len += sprintf(buf + len, "0x%02x 0x%08x\n",
|
||||
pin->nid, pin->cfg);
|
||||
}
|
||||
|
@ -217,10 +217,10 @@ static ssize_t init_verbs_show(struct device *dev,
|
|||
char *buf)
|
||||
{
|
||||
struct hda_codec *codec = dev_get_drvdata(dev);
|
||||
const struct hda_verb *v;
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < codec->init_verbs.used; i++) {
|
||||
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
|
||||
snd_array_for_each(&codec->init_verbs, i, v) {
|
||||
len += snprintf(buf + len, PAGE_SIZE - len,
|
||||
"0x%02x 0x%03x 0x%04x\n",
|
||||
v->nid, v->verb, v->param);
|
||||
|
@ -267,10 +267,10 @@ static ssize_t hints_show(struct device *dev,
|
|||
char *buf)
|
||||
{
|
||||
struct hda_codec *codec = dev_get_drvdata(dev);
|
||||
const struct hda_hint *hint;
|
||||
int i, len = 0;
|
||||
mutex_lock(&codec->user_mutex);
|
||||
for (i = 0; i < codec->hints.used; i++) {
|
||||
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
|
||||
snd_array_for_each(&codec->hints, i, hint) {
|
||||
len += snprintf(buf + len, PAGE_SIZE - len,
|
||||
"%s = %s\n", hint->key, hint->val);
|
||||
}
|
||||
|
@ -280,10 +280,10 @@ static ssize_t hints_show(struct device *dev,
|
|||
|
||||
static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
|
||||
{
|
||||
struct hda_hint *hint;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->hints.used; i++) {
|
||||
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
|
||||
snd_array_for_each(&codec->hints, i, hint) {
|
||||
if (!strcmp(hint->key, key))
|
||||
return hint;
|
||||
}
|
||||
|
@ -783,13 +783,13 @@ void snd_hda_sysfs_init(struct hda_codec *codec)
|
|||
void snd_hda_sysfs_clear(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef CONFIG_SND_HDA_RECONFIG
|
||||
struct hda_hint *hint;
|
||||
int i;
|
||||
|
||||
/* clear init verbs */
|
||||
snd_array_free(&codec->init_verbs);
|
||||
/* clear hints */
|
||||
for (i = 0; i < codec->hints.used; i++) {
|
||||
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
|
||||
snd_array_for_each(&codec->hints, i, hint) {
|
||||
kfree(hint->key); /* we don't need to free hint->val */
|
||||
}
|
||||
snd_array_free(&codec->hints);
|
||||
|
|
95
sound/pci/hda/hp_x360_helper.c
Normal file
95
sound/pci/hda/hp_x360_helper.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Fixes for HP X360 laptops with top B&O speakers
|
||||
* to be included from codec driver
|
||||
*/
|
||||
|
||||
static void alc295_fixup_hp_top_speakers(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
static const struct hda_pintbl pincfgs[] = {
|
||||
{ 0x17, 0x90170110 },
|
||||
{ }
|
||||
};
|
||||
static const struct coef_fw alc295_hp_speakers_coefs[] = {
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
|
||||
WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024),
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
snd_hda_apply_pincfgs(codec, pincfgs);
|
||||
alc295_fixup_disable_dac3(codec, fix, action);
|
||||
break;
|
||||
case HDA_FIXUP_ACT_INIT:
|
||||
alc_process_coef_fw(codec, alc295_hp_speakers_coefs);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*/
|
||||
|
||||
#ifndef __HDAC_LOCAL_H
|
||||
#define __HDAC_LOCAL_H
|
||||
|
||||
int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm);
|
||||
|
||||
#define get_wcaps(codec, nid) \
|
||||
hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP)
|
||||
/* get the widget type from widget capability bits */
|
||||
static inline int get_wcaps_type(unsigned int wcaps)
|
||||
{
|
||||
if (!wcaps)
|
||||
return -1; /* invalid type */
|
||||
return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
#define get_pin_caps(codec, nid) \
|
||||
hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)
|
||||
|
||||
static inline
|
||||
unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
|
||||
return -1;
|
||||
return val;
|
||||
}
|
||||
|
||||
#define get_amp_caps(codec, nid, dir) \
|
||||
hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \
|
||||
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP)
|
||||
|
||||
#define get_power_caps(codec, nid) \
|
||||
hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)
|
||||
|
||||
#endif /* __HDAC_LOCAL_H */
|
File diff suppressed because it is too large
Load diff
|
@ -588,6 +588,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
|
|||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
struct snd_kcontrol_new *kctl;
|
||||
int i;
|
||||
|
||||
if (action != HDA_FIXUP_ACT_PROBE)
|
||||
|
@ -606,9 +607,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
|
|||
snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
|
||||
|
||||
/* override mic boost control */
|
||||
for (i = 0; i < spec->gen.kctls.used; i++) {
|
||||
struct snd_kcontrol_new *kctl =
|
||||
snd_array_elem(&spec->gen.kctls, i);
|
||||
snd_array_for_each(&spec->gen.kctls, i, kctl) {
|
||||
if (!strcmp(kctl->name, "Mic Boost Volume")) {
|
||||
kctl->put = olpc_xo_mic_boost_put;
|
||||
break;
|
||||
|
@ -965,6 +964,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
|
||||
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
|
||||
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
|
||||
|
@ -998,6 +998,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
|
|||
{ .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
|
||||
{ .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
|
||||
{ .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
|
||||
{ .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -510,7 +510,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
|
|||
|
||||
snd_info_set_text_ops(entry, per_pin, print_eld_info);
|
||||
entry->c.text.write = write_eld_info;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
per_pin->proc_entry = entry;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2830,6 +2830,7 @@ static int find_ext_mic_pin(struct hda_codec *codec);
|
|||
|
||||
static void alc286_shutup(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
int mic_pin = find_ext_mic_pin(codec);
|
||||
/* don't shut up pins when unloading the driver; otherwise it breaks
|
||||
|
@ -2837,8 +2838,7 @@ static void alc286_shutup(struct hda_codec *codec)
|
|||
*/
|
||||
if (codec->bus->shutdown)
|
||||
return;
|
||||
for (i = 0; i < codec->init_pins.used; i++) {
|
||||
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
|
||||
snd_array_for_each(&codec->init_pins, i, pin) {
|
||||
/* use read here for syncing after issuing each verb */
|
||||
if (pin->nid != mic_pin)
|
||||
snd_hda_codec_read(codec, pin->nid, 0,
|
||||
|
@ -3653,30 +3653,37 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action, hda_nid_t pin)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
spec->mute_led_polarity = 0;
|
||||
spec->mute_led_nid = 0x18;
|
||||
spec->mute_led_nid = pin;
|
||||
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
|
||||
spec->gen.vmaster_mute_enum = 1;
|
||||
codec->power_filter = led_power_filter;
|
||||
}
|
||||
}
|
||||
|
||||
static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
|
||||
}
|
||||
|
||||
static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
spec->mute_led_polarity = 0;
|
||||
spec->mute_led_nid = 0x19;
|
||||
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
|
||||
spec->gen.vmaster_mute_enum = 1;
|
||||
codec->power_filter = led_power_filter;
|
||||
}
|
||||
alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
|
||||
}
|
||||
|
||||
static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
|
||||
}
|
||||
|
||||
/* update LED status via GPIO */
|
||||
|
@ -5387,6 +5394,9 @@ static void alc274_fixup_bind_dacs(struct hda_codec *codec,
|
|||
/* for dell wmi mic mute led */
|
||||
#include "dell_wmi_helper.c"
|
||||
|
||||
/* for alc295_fixup_hp_top_speakers */
|
||||
#include "hp_x360_helper.c"
|
||||
|
||||
enum {
|
||||
ALC269_FIXUP_SONY_VAIO,
|
||||
ALC275_FIXUP_SONY_VAIO_GPIO2,
|
||||
|
@ -5413,6 +5423,7 @@ enum {
|
|||
ALC269_FIXUP_HP_MUTE_LED,
|
||||
ALC269_FIXUP_HP_MUTE_LED_MIC1,
|
||||
ALC269_FIXUP_HP_MUTE_LED_MIC2,
|
||||
ALC269_FIXUP_HP_MUTE_LED_MIC3,
|
||||
ALC269_FIXUP_HP_GPIO_LED,
|
||||
ALC269_FIXUP_HP_GPIO_MIC1_LED,
|
||||
ALC269_FIXUP_HP_LINE1_MIC1_LED,
|
||||
|
@ -5506,6 +5517,7 @@ enum {
|
|||
ALC298_FIXUP_TPT470_DOCK,
|
||||
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
|
||||
ALC255_FIXUP_DELL_HEADSET_MIC,
|
||||
ALC295_FIXUP_HP_X360,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc269_fixups[] = {
|
||||
|
@ -5672,6 +5684,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_hp_mute_led_mic2,
|
||||
},
|
||||
[ALC269_FIXUP_HP_MUTE_LED_MIC3] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_hp_mute_led_mic3,
|
||||
},
|
||||
[ALC269_FIXUP_HP_GPIO_LED] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_hp_gpio_led,
|
||||
|
@ -6375,6 +6391,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_HEADSET_MIC
|
||||
},
|
||||
[ALC295_FIXUP_HP_X360] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc295_fixup_hp_top_speakers,
|
||||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
|
@ -6494,6 +6516,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
|
||||
SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
|
||||
SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
|
||||
SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
|
||||
|
@ -6580,7 +6603,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3138, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
|
||||
SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
|
||||
|
@ -6752,6 +6774,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{0x1b, 0x01111010},
|
||||
{0x1e, 0x01451130},
|
||||
{0x21, 0x02211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
|
||||
{0x12, 0x90a60140},
|
||||
{0x14, 0x90170110},
|
||||
{0x19, 0x02a11030},
|
||||
{0x21, 0x02211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0x90a60140},
|
||||
{0x14, 0x90170110},
|
||||
|
|
|
@ -662,7 +662,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
|
|||
struct snd_info_entry *entry;
|
||||
if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
|
||||
snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->c.text.write = wm_proc_regs_write;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -926,7 +926,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
|
|||
struct snd_info_entry *entry;
|
||||
if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
|
||||
snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->c.text.write = wm_proc_regs_write;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ void lola_proc_debug_new(struct lola *chip)
|
|||
snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
|
||||
if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
|
||||
snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
entry->c.text.write = lola_proc_codec_rw_write;
|
||||
}
|
||||
if (!snd_card_proc_new(chip->card, "regs", &entry))
|
||||
|
|
|
@ -1052,10 +1052,10 @@ static int add_controls(struct oxygen *chip,
|
|||
[CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
|
||||
[CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
|
||||
};
|
||||
unsigned int i, j;
|
||||
unsigned int i;
|
||||
struct snd_kcontrol_new template;
|
||||
struct snd_kcontrol *ctl;
|
||||
int err;
|
||||
int j, err;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
template = controls[i];
|
||||
|
@ -1086,11 +1086,11 @@ static int add_controls(struct oxygen *chip,
|
|||
err = snd_ctl_add(chip->card, ctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (j = 0; j < CONTROL_COUNT; ++j)
|
||||
if (!strcmp(ctl->id.name, known_ctl_names[j])) {
|
||||
chip->controls[j] = ctl;
|
||||
ctl->private_free = oxygen_any_ctl_free;
|
||||
}
|
||||
j = match_string(known_ctl_names, CONTROL_COUNT, ctl->id.name);
|
||||
if (j >= 0) {
|
||||
chip->controls[j] = ctl;
|
||||
ctl->private_free = oxygen_any_ctl_free;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1465,7 +1465,7 @@ static void pcxhr_proc_init(struct snd_pcxhr *chip)
|
|||
!snd_card_proc_new(chip->card, "gpio", &entry)) {
|
||||
snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
|
||||
entry->c.text.write = pcxhr_proc_gpo_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
entry->mode |= 0200;
|
||||
}
|
||||
if (!snd_card_proc_new(chip->card, "ltc", &entry))
|
||||
snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc);
|
||||
|
|
|
@ -1733,10 +1733,10 @@ static ssize_t cs43130_show_ac_r(struct device *dev,
|
|||
return cs43130_show_ac(dev, buf, HP_RIGHT);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(hpload_dc_l, S_IRUGO, cs43130_show_dc_l, NULL);
|
||||
static DEVICE_ATTR(hpload_dc_r, S_IRUGO, cs43130_show_dc_r, NULL);
|
||||
static DEVICE_ATTR(hpload_ac_l, S_IRUGO, cs43130_show_ac_l, NULL);
|
||||
static DEVICE_ATTR(hpload_ac_r, S_IRUGO, cs43130_show_ac_r, NULL);
|
||||
static DEVICE_ATTR(hpload_dc_l, 0444, cs43130_show_dc_l, NULL);
|
||||
static DEVICE_ATTR(hpload_dc_r, 0444, cs43130_show_dc_r, NULL);
|
||||
static DEVICE_ATTR(hpload_ac_l, 0444, cs43130_show_ac_l, NULL);
|
||||
static DEVICE_ATTR(hpload_ac_r, 0444, cs43130_show_ac_r, NULL);
|
||||
|
||||
static struct reg_sequence hp_en_cal_seq[] = {
|
||||
{CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
|
||||
|
|
|
@ -627,22 +627,21 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
|
|||
if (!root)
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted))
|
||||
if (!debugfs_create_bool("booted", 0444, root, &dsp->booted))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
|
||||
if (!debugfs_create_bool("running", 0444, root, &dsp->running))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
|
||||
if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_x32("fw_version", S_IRUGO, root,
|
||||
&dsp->fw_id_version))
|
||||
if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version))
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
|
||||
if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
|
||||
S_IRUGO, root, dsp,
|
||||
0444, root, dsp,
|
||||
&wm_adsp_debugfs_fops[i].fops))
|
||||
goto err;
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
|
|||
if (!ssi_dbg->dbg_dir)
|
||||
return -ENOMEM;
|
||||
|
||||
ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
|
||||
ssi_dbg->dbg_stats = debugfs_create_file("stats", 0444,
|
||||
ssi_dbg->dbg_dir, ssi_dbg,
|
||||
&fsl_ssi_stats_ops);
|
||||
if (!ssi_dbg->dbg_stats) {
|
||||
|
|
|
@ -413,7 +413,7 @@ int register_sound_special_device(const struct file_operations *fops, int unit,
|
|||
break;
|
||||
}
|
||||
return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
|
||||
name, S_IRUSR | S_IWUSR, dev);
|
||||
name, 0600, dev);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(register_sound_special_device);
|
||||
|
@ -440,7 +440,7 @@ EXPORT_SYMBOL(register_sound_special);
|
|||
int register_sound_mixer(const struct file_operations *fops, int dev)
|
||||
{
|
||||
return sound_insert_unit(&chains[0], fops, dev, 0, 128,
|
||||
"mixer", S_IRUSR | S_IWUSR, NULL);
|
||||
"mixer", 0600, NULL);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(register_sound_mixer);
|
||||
|
@ -468,7 +468,7 @@ EXPORT_SYMBOL(register_sound_mixer);
|
|||
int register_sound_dsp(const struct file_operations *fops, int dev)
|
||||
{
|
||||
return sound_insert_unit(&chains[3], fops, dev, 3, 131,
|
||||
"dsp", S_IWUSR | S_IRUSR, NULL);
|
||||
"dsp", 0600, NULL);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(register_sound_dsp);
|
||||
|
|
|
@ -2518,7 +2518,7 @@ static void snd_dbri_proc(struct snd_card *card)
|
|||
#ifdef DBRI_DEBUG
|
||||
if (!snd_card_proc_new(card, "debug", &entry)) {
|
||||
snd_info_set_text_ops(entry, dbri, dbri_debug_read);
|
||||
entry->mode = S_IFREG | S_IRUGO; /* Readable only. */
|
||||
entry->mode = S_IFREG | 0444; /* Readable only. */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2542,7 +2542,7 @@ static int snd_dbri_create(struct snd_card *card,
|
|||
dbri->irq = irq;
|
||||
|
||||
dbri->dma = dma_zalloc_coherent(&op->dev, sizeof(struct dbri_dma),
|
||||
&dbri->dma_dvma, GFP_ATOMIC);
|
||||
&dbri->dma_dvma, GFP_KERNEL);
|
||||
if (!dbri->dma)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
248
sound/usb/card.c
248
sound/usb/card.c
|
@ -86,6 +86,8 @@ static bool ignore_ctl_error;
|
|||
static bool autoclock = true;
|
||||
static char *quirk_alias[SNDRV_CARDS];
|
||||
|
||||
bool snd_usb_use_vmalloc = true;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
|
@ -105,6 +107,8 @@ module_param(autoclock, bool, 0444);
|
|||
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
|
||||
module_param_array(quirk_alias, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
|
||||
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
|
||||
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
|
||||
|
||||
/*
|
||||
* we keep the snd_usb_audio_t instances by ourselves for merging
|
||||
|
@ -221,32 +225,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
struct usb_device *dev = chip->dev;
|
||||
struct usb_host_interface *host_iface;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
void *control_header;
|
||||
int i, protocol;
|
||||
int rest_bytes;
|
||||
|
||||
/* find audiocontrol interface */
|
||||
host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
|
||||
control_header = snd_usb_find_csint_desc(host_iface->extra,
|
||||
host_iface->extralen,
|
||||
NULL, UAC_HEADER);
|
||||
altsd = get_iface_desc(host_iface);
|
||||
protocol = altsd->bInterfaceProtocol;
|
||||
|
||||
if (!control_header) {
|
||||
dev_err(&dev->dev, "cannot find UAC_HEADER\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
|
||||
control_header;
|
||||
|
||||
/* just to be sure -- this shouldn't hit at all */
|
||||
if (rest_bytes <= 0) {
|
||||
dev_err(&dev->dev, "invalid control header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
default:
|
||||
dev_warn(&dev->dev,
|
||||
|
@ -255,7 +240,25 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
/* fall through */
|
||||
|
||||
case UAC_VERSION_1: {
|
||||
struct uac1_ac_header_descriptor *h1 = control_header;
|
||||
struct uac1_ac_header_descriptor *h1;
|
||||
int rest_bytes;
|
||||
|
||||
h1 = snd_usb_find_csint_desc(host_iface->extra,
|
||||
host_iface->extralen,
|
||||
NULL, UAC_HEADER);
|
||||
if (!h1) {
|
||||
dev_err(&dev->dev, "cannot find UAC_HEADER\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rest_bytes = (void *)(host_iface->extra +
|
||||
host_iface->extralen) - (void *)h1;
|
||||
|
||||
/* just to be sure -- this shouldn't hit at all */
|
||||
if (rest_bytes <= 0) {
|
||||
dev_err(&dev->dev, "invalid control header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rest_bytes < sizeof(*h1)) {
|
||||
dev_err(&dev->dev, "too short v1 buffer descriptor\n");
|
||||
|
@ -308,6 +311,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (protocol == UAC_VERSION_3) {
|
||||
int badd = assoc->bFunctionSubClass;
|
||||
|
||||
if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 &&
|
||||
(badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO ||
|
||||
badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) {
|
||||
dev_err(&dev->dev,
|
||||
"Unsupported UAC3 BADD profile\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->badd_profile = badd;
|
||||
}
|
||||
|
||||
for (i = 0; i < assoc->bInterfaceCount; i++) {
|
||||
int intf = assoc->bFirstInterface + i;
|
||||
|
||||
|
@ -329,8 +346,9 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
*
|
||||
*/
|
||||
|
||||
static int snd_usb_audio_free(struct snd_usb_audio *chip)
|
||||
static void snd_usb_audio_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_usb_audio *chip = card->private_data;
|
||||
struct snd_usb_endpoint *ep, *n;
|
||||
|
||||
list_for_each_entry_safe(ep, n, &chip->ep_list, list)
|
||||
|
@ -339,108 +357,51 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
|
|||
mutex_destroy(&chip->mutex);
|
||||
if (!atomic_read(&chip->shutdown))
|
||||
dev_set_drvdata(&chip->dev->dev, NULL);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_usb_audio_dev_free(struct snd_device *device)
|
||||
static void usb_audio_make_shortname(struct usb_device *dev,
|
||||
struct snd_usb_audio *chip,
|
||||
const struct snd_usb_audio_quirk *quirk)
|
||||
{
|
||||
struct snd_usb_audio *chip = device->device_data;
|
||||
return snd_usb_audio_free(chip);
|
||||
}
|
||||
struct snd_card *card = chip->card;
|
||||
|
||||
/*
|
||||
* create a chip instance and set its names.
|
||||
*/
|
||||
static int snd_usb_audio_create(struct usb_interface *intf,
|
||||
struct usb_device *dev, int idx,
|
||||
const struct snd_usb_audio_quirk *quirk,
|
||||
unsigned int usb_id,
|
||||
struct snd_usb_audio **rchip)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_usb_audio *chip;
|
||||
int err, len;
|
||||
char component[14];
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_usb_audio_dev_free,
|
||||
};
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
switch (snd_usb_get_speed(dev)) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_WIRELESS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
|
||||
return -ENXIO;
|
||||
if (quirk && quirk->product_name && *quirk->product_name) {
|
||||
strlcpy(card->shortname, quirk->product_name,
|
||||
sizeof(card->shortname));
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
|
||||
0, &card);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "cannot create card instance %d\n", idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (! chip) {
|
||||
snd_card_free(card);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&chip->mutex);
|
||||
init_waitqueue_head(&chip->shutdown_wait);
|
||||
chip->index = idx;
|
||||
chip->dev = dev;
|
||||
chip->card = card;
|
||||
chip->setup = device_setup[idx];
|
||||
chip->autoclock = autoclock;
|
||||
atomic_set(&chip->active, 1); /* avoid autopm during probing */
|
||||
atomic_set(&chip->usage_count, 0);
|
||||
atomic_set(&chip->shutdown, 0);
|
||||
|
||||
chip->usb_id = usb_id;
|
||||
INIT_LIST_HEAD(&chip->pcm_list);
|
||||
INIT_LIST_HEAD(&chip->ep_list);
|
||||
INIT_LIST_HEAD(&chip->midi_list);
|
||||
INIT_LIST_HEAD(&chip->mixer_list);
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
|
||||
snd_usb_audio_free(chip);
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(card->driver, "USB-Audio");
|
||||
sprintf(component, "USB%04x:%04x",
|
||||
USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
|
||||
snd_component_add(card, component);
|
||||
|
||||
/* retrieve the device string as shortname */
|
||||
if (quirk && quirk->product_name && *quirk->product_name) {
|
||||
strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
|
||||
} else {
|
||||
if (!dev->descriptor.iProduct ||
|
||||
usb_string(dev, dev->descriptor.iProduct,
|
||||
card->shortname, sizeof(card->shortname)) <= 0) {
|
||||
/* no name available from anywhere, so use ID */
|
||||
sprintf(card->shortname, "USB Device %#04x:%#04x",
|
||||
USB_ID_VENDOR(chip->usb_id),
|
||||
USB_ID_PRODUCT(chip->usb_id));
|
||||
}
|
||||
if (!dev->descriptor.iProduct ||
|
||||
usb_string(dev, dev->descriptor.iProduct,
|
||||
card->shortname, sizeof(card->shortname)) <= 0) {
|
||||
/* no name available from anywhere, so use ID */
|
||||
sprintf(card->shortname, "USB Device %#04x:%#04x",
|
||||
USB_ID_VENDOR(chip->usb_id),
|
||||
USB_ID_PRODUCT(chip->usb_id));
|
||||
}
|
||||
|
||||
strim(card->shortname);
|
||||
}
|
||||
|
||||
static void usb_audio_make_longname(struct usb_device *dev,
|
||||
struct snd_usb_audio *chip,
|
||||
const struct snd_usb_audio_quirk *quirk)
|
||||
{
|
||||
struct snd_card *card = chip->card;
|
||||
int len;
|
||||
|
||||
/* shortcut - if any pre-defined string is given, use it */
|
||||
if (quirk && quirk->profile_name && *quirk->profile_name) {
|
||||
strlcpy(card->longname, quirk->profile_name,
|
||||
sizeof(card->longname));
|
||||
return;
|
||||
}
|
||||
strim(card->shortname);
|
||||
|
||||
/* retrieve the vendor and device strings as longname */
|
||||
if (quirk && quirk->vendor_name && *quirk->vendor_name) {
|
||||
len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
|
||||
} else {
|
||||
/* retrieve the vendor and device strings as longname */
|
||||
if (dev->descriptor.iManufacturer)
|
||||
len = usb_string(dev, dev->descriptor.iManufacturer,
|
||||
card->longname, sizeof(card->longname));
|
||||
|
@ -480,6 +441,71 @@ static int snd_usb_audio_create(struct usb_interface *intf,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* create a chip instance and set its names.
|
||||
*/
|
||||
static int snd_usb_audio_create(struct usb_interface *intf,
|
||||
struct usb_device *dev, int idx,
|
||||
const struct snd_usb_audio_quirk *quirk,
|
||||
unsigned int usb_id,
|
||||
struct snd_usb_audio **rchip)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_usb_audio *chip;
|
||||
int err;
|
||||
char component[14];
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
switch (snd_usb_get_speed(dev)) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_WIRELESS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
|
||||
sizeof(*chip), &card);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "cannot create card instance %d\n", idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
chip = card->private_data;
|
||||
mutex_init(&chip->mutex);
|
||||
init_waitqueue_head(&chip->shutdown_wait);
|
||||
chip->index = idx;
|
||||
chip->dev = dev;
|
||||
chip->card = card;
|
||||
chip->setup = device_setup[idx];
|
||||
chip->autoclock = autoclock;
|
||||
atomic_set(&chip->active, 1); /* avoid autopm during probing */
|
||||
atomic_set(&chip->usage_count, 0);
|
||||
atomic_set(&chip->shutdown, 0);
|
||||
|
||||
chip->usb_id = usb_id;
|
||||
INIT_LIST_HEAD(&chip->pcm_list);
|
||||
INIT_LIST_HEAD(&chip->ep_list);
|
||||
INIT_LIST_HEAD(&chip->midi_list);
|
||||
INIT_LIST_HEAD(&chip->mixer_list);
|
||||
|
||||
card->private_free = snd_usb_audio_free;
|
||||
|
||||
strcpy(card->driver, "USB-Audio");
|
||||
sprintf(component, "USB%04x:%04x",
|
||||
USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
|
||||
snd_component_add(card, component);
|
||||
|
||||
usb_audio_make_shortname(dev, chip, quirk);
|
||||
usb_audio_make_longname(dev, chip, quirk);
|
||||
|
||||
snd_usb_audio_create_proc(chip);
|
||||
|
||||
|
|
|
@ -443,10 +443,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
|||
data[0] = rate;
|
||||
data[1] = rate >> 8;
|
||||
data[2] = rate >> 16;
|
||||
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data))) < 0) {
|
||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data));
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
|
||||
iface, fmt->altsetting, rate, ep);
|
||||
return err;
|
||||
|
@ -460,10 +461,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
|||
if (chip->sample_rate_read_error > 2)
|
||||
return 0;
|
||||
|
||||
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data))) < 0) {
|
||||
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data));
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
|
||||
iface, fmt->altsetting, ep);
|
||||
chip->sample_rate_read_error++;
|
||||
|
@ -587,8 +589,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
|||
default:
|
||||
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
||||
|
||||
case UAC_VERSION_2:
|
||||
case UAC_VERSION_3:
|
||||
if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
|
||||
if (rate != UAC3_BADD_SAMPLING_RATE)
|
||||
return -ENXIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
/* fall through */
|
||||
case UAC_VERSION_2:
|
||||
return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,12 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
|
|||
* retrieve usb_interface descriptor from the host interface
|
||||
* (conditional for compatibility with the older API)
|
||||
*/
|
||||
#ifndef get_iface_desc
|
||||
#define get_iface_desc(iface) (&(iface)->desc)
|
||||
#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc)
|
||||
#define get_ep_desc(ep) (&(ep)->desc)
|
||||
#define get_cfg_desc(cfg) (&(cfg)->desc)
|
||||
#endif
|
||||
|
||||
#ifndef snd_usb_get_speed
|
||||
#define snd_usb_get_speed(dev) ((dev)->speed)
|
||||
#endif
|
||||
|
||||
static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
|
||||
{
|
||||
|
|
|
@ -112,14 +112,12 @@ enum {
|
|||
#include "mixer_maps.c"
|
||||
|
||||
static const struct usbmix_name_map *
|
||||
find_map(struct mixer_build *state, int unitid, int control)
|
||||
find_map(const struct usbmix_name_map *p, int unitid, int control)
|
||||
{
|
||||
const struct usbmix_name_map *p = state->map;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
for (p = state->map; p->id; p++) {
|
||||
for (; p->id; p++) {
|
||||
if (p->id == unitid &&
|
||||
(!control || !p->control || control == p->control))
|
||||
return p;
|
||||
|
@ -201,10 +199,10 @@ static void *find_audio_control_unit(struct mixer_build *state,
|
|||
/*
|
||||
* copy a string with the given id
|
||||
*/
|
||||
static int snd_usb_copy_string_desc(struct mixer_build *state,
|
||||
static int snd_usb_copy_string_desc(struct snd_usb_audio *chip,
|
||||
int index, char *buf, int maxlen)
|
||||
{
|
||||
int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
|
||||
int len = usb_string(chip->dev, index, buf, maxlen - 1);
|
||||
|
||||
if (len < 0)
|
||||
return 0;
|
||||
|
@ -600,7 +598,8 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list,
|
|||
|
||||
while (snd_ctl_find_id(mixer->chip->card, &kctl->id))
|
||||
kctl->id.index++;
|
||||
if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) {
|
||||
err = snd_ctl_add(mixer->chip->card, kctl);
|
||||
if (err < 0) {
|
||||
usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n",
|
||||
err);
|
||||
return err;
|
||||
|
@ -658,14 +657,14 @@ static struct iterm_name_combo {
|
|||
{ 0 },
|
||||
};
|
||||
|
||||
static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm,
|
||||
static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm,
|
||||
unsigned char *name, int maxlen, int term_only)
|
||||
{
|
||||
struct iterm_name_combo *names;
|
||||
int len;
|
||||
|
||||
if (iterm->name) {
|
||||
len = snd_usb_copy_string_desc(state, iterm->name,
|
||||
len = snd_usb_copy_string_desc(chip, iterm->name,
|
||||
name, maxlen);
|
||||
if (len)
|
||||
return len;
|
||||
|
@ -718,6 +717,66 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get logical cluster information for UAC3 devices.
|
||||
*/
|
||||
static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id)
|
||||
{
|
||||
struct uac3_cluster_header_descriptor c_header;
|
||||
int err;
|
||||
|
||||
err = snd_usb_ctl_msg(state->chip->dev,
|
||||
usb_rcvctrlpipe(state->chip->dev, 0),
|
||||
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
cluster_id,
|
||||
snd_usb_ctrl_intf(state->chip),
|
||||
&c_header, sizeof(c_header));
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (err != sizeof(c_header)) {
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return c_header.bNrChannels;
|
||||
|
||||
error:
|
||||
usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get number of channels for a Mixer Unit.
|
||||
*/
|
||||
static int uac_mixer_unit_get_channels(struct mixer_build *state,
|
||||
struct uac_mixer_unit_descriptor *desc)
|
||||
{
|
||||
int mu_channels;
|
||||
|
||||
if (desc->bLength < 11)
|
||||
return -EINVAL;
|
||||
if (!desc->bNrInPins)
|
||||
return -EINVAL;
|
||||
|
||||
switch (state->mixer->protocol) {
|
||||
case UAC_VERSION_1:
|
||||
case UAC_VERSION_2:
|
||||
default:
|
||||
mu_channels = uac_mixer_unit_bNrChannels(desc);
|
||||
break;
|
||||
case UAC_VERSION_3:
|
||||
mu_channels = get_cluster_channels_v3(state,
|
||||
uac3_mixer_unit_wClusterDescrID(desc));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mu_channels)
|
||||
return -EINVAL;
|
||||
|
||||
return mu_channels;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the source unit recursively until it reaches to a terminal
|
||||
* or a branched unit.
|
||||
|
@ -844,8 +903,12 @@ static int check_input_term(struct mixer_build *state, int id,
|
|||
term->id = id;
|
||||
term->type = le16_to_cpu(d->wTerminalType);
|
||||
|
||||
/* REVISIT: UAC3 IT doesn't have channels/cfg */
|
||||
term->channels = 0;
|
||||
err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
|
||||
if (err < 0)
|
||||
return err;
|
||||
term->channels = err;
|
||||
|
||||
/* REVISIT: UAC3 IT doesn't have channels cfg */
|
||||
term->chconfig = 0;
|
||||
|
||||
term->name = le16_to_cpu(d->wTerminalDescrStr);
|
||||
|
@ -865,6 +928,18 @@ static int check_input_term(struct mixer_build *state, int id,
|
|||
term->name = le16_to_cpu(d->wClockSourceStr);
|
||||
return 0;
|
||||
}
|
||||
case UAC3_MIXER_UNIT: {
|
||||
struct uac_mixer_unit_descriptor *d = p1;
|
||||
|
||||
err = uac_mixer_unit_get_channels(state, d);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
term->channels = err;
|
||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1258,6 +1333,51 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* get the connectors status and report it as boolean type */
|
||||
static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
||||
struct snd_usb_audio *chip = cval->head.mixer->chip;
|
||||
int idx = 0, validx, ret, val;
|
||||
|
||||
validx = cval->control << 8 | 0;
|
||||
|
||||
ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
||||
if (cval->head.mixer->protocol == UAC_VERSION_2) {
|
||||
struct uac2_connectors_ctl_blk uac2_conn;
|
||||
|
||||
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, idx, &uac2_conn, sizeof(uac2_conn));
|
||||
val = !!uac2_conn.bNrChannels;
|
||||
} else { /* UAC_VERSION_3 */
|
||||
struct uac3_insertion_ctl_blk uac3_conn;
|
||||
|
||||
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, idx, &uac3_conn, sizeof(uac3_conn));
|
||||
val = !!uac3_conn.bmConInserted;
|
||||
}
|
||||
|
||||
snd_usb_unlock_shutdown(chip);
|
||||
|
||||
if (ret < 0) {
|
||||
error:
|
||||
usb_audio_err(chip,
|
||||
"cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
|
||||
UAC_GET_CUR, validx, idx, cval->val_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new usb_feature_unit_ctl = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "", /* will be filled later manually */
|
||||
|
@ -1288,6 +1408,15 @@ static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = {
|
|||
.put = NULL,
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new usb_connector_ctl_ro = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
|
||||
.name = "", /* will be filled later manually */
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = mixer_ctl_connector_get,
|
||||
.put = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This symbol is exported in order to allow the mixer quirks to
|
||||
* hook up to the standard feature unit control mechanism
|
||||
|
@ -1341,16 +1470,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||
unsigned int ctl_mask, int control,
|
||||
struct usb_audio_term *iterm, int unitid,
|
||||
int readonly_mask)
|
||||
static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
const struct usbmix_name_map *imap,
|
||||
unsigned int ctl_mask, int control,
|
||||
struct usb_audio_term *iterm,
|
||||
struct usb_audio_term *oterm,
|
||||
int unitid, int nameid, int readonly_mask)
|
||||
{
|
||||
struct uac_feature_unit_descriptor *desc = raw_desc;
|
||||
struct usb_feature_control_info *ctl_info;
|
||||
unsigned int len = 0;
|
||||
int mapped_name = 0;
|
||||
int nameid = uac_feature_unit_iFeature(desc);
|
||||
struct snd_kcontrol *kctl;
|
||||
struct usb_mixer_elem_info *cval;
|
||||
const struct usbmix_name_map *map;
|
||||
|
@ -1361,14 +1490,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
return;
|
||||
}
|
||||
|
||||
map = find_map(state, unitid, control);
|
||||
map = find_map(imap, unitid, control);
|
||||
if (check_ignored_ctl(map))
|
||||
return;
|
||||
|
||||
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
|
||||
if (!cval)
|
||||
return;
|
||||
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
|
||||
snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
|
||||
cval->control = control;
|
||||
cval->cmask = ctl_mask;
|
||||
|
||||
|
@ -1377,7 +1506,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
kfree(cval);
|
||||
return;
|
||||
}
|
||||
if (state->mixer->protocol == UAC_VERSION_1)
|
||||
if (mixer->protocol == UAC_VERSION_1)
|
||||
cval->val_type = ctl_info->type;
|
||||
else /* UAC_VERSION_2 */
|
||||
cval->val_type = ctl_info->type_uac2 >= 0 ?
|
||||
|
@ -1406,7 +1535,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
||||
|
||||
if (!kctl) {
|
||||
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
||||
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
|
||||
kfree(cval);
|
||||
return;
|
||||
}
|
||||
|
@ -1415,7 +1544,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
|
||||
mapped_name = len != 0;
|
||||
if (!len && nameid)
|
||||
len = snd_usb_copy_string_desc(state, nameid,
|
||||
len = snd_usb_copy_string_desc(mixer->chip, nameid,
|
||||
kctl->id.name, sizeof(kctl->id.name));
|
||||
|
||||
switch (control) {
|
||||
|
@ -1430,10 +1559,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
* - otherwise, anonymous name.
|
||||
*/
|
||||
if (!len) {
|
||||
len = get_term_name(state, iterm, kctl->id.name,
|
||||
sizeof(kctl->id.name), 1);
|
||||
if (!len)
|
||||
len = get_term_name(state, &state->oterm,
|
||||
if (iterm)
|
||||
len = get_term_name(mixer->chip, iterm,
|
||||
kctl->id.name,
|
||||
sizeof(kctl->id.name), 1);
|
||||
if (!len && oterm)
|
||||
len = get_term_name(mixer->chip, oterm,
|
||||
kctl->id.name,
|
||||
sizeof(kctl->id.name), 1);
|
||||
if (!len)
|
||||
|
@ -1442,15 +1573,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
}
|
||||
|
||||
if (!mapped_name)
|
||||
check_no_speaker_on_headset(kctl, state->mixer->chip->card);
|
||||
check_no_speaker_on_headset(kctl, mixer->chip->card);
|
||||
|
||||
/*
|
||||
* determine the stream direction:
|
||||
* if the connected output is USB stream, then it's likely a
|
||||
* capture stream. otherwise it should be playback (hopefully :)
|
||||
*/
|
||||
if (!mapped_name && !(state->oterm.type >> 16)) {
|
||||
if ((state->oterm.type & 0xff00) == 0x0100)
|
||||
if (!mapped_name && oterm && !(oterm->type >> 16)) {
|
||||
if ((oterm->type & 0xff00) == 0x0100)
|
||||
append_ctl_name(kctl, " Capture");
|
||||
else
|
||||
append_ctl_name(kctl, " Playback");
|
||||
|
@ -1478,7 +1609,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
}
|
||||
}
|
||||
|
||||
snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl);
|
||||
snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl);
|
||||
|
||||
range = (cval->max - cval->min) / cval->res;
|
||||
/*
|
||||
|
@ -1487,26 +1618,46 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
* devices. It will definitively catch all buggy Logitech devices.
|
||||
*/
|
||||
if (range > 384) {
|
||||
usb_audio_warn(state->chip,
|
||||
usb_audio_warn(mixer->chip,
|
||||
"Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
|
||||
range);
|
||||
usb_audio_warn(state->chip,
|
||||
usb_audio_warn(mixer->chip,
|
||||
"[%d] FU [%s] ch = %d, val = %d/%d/%d",
|
||||
cval->head.id, kctl->id.name, cval->channels,
|
||||
cval->min, cval->max, cval->res);
|
||||
}
|
||||
|
||||
usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
|
||||
usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
|
||||
cval->head.id, kctl->id.name, cval->channels,
|
||||
cval->min, cval->max, cval->res);
|
||||
snd_usb_mixer_add_control(&cval->head, kctl);
|
||||
}
|
||||
|
||||
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||
unsigned int ctl_mask, int control,
|
||||
struct usb_audio_term *iterm, int unitid,
|
||||
int readonly_mask)
|
||||
{
|
||||
struct uac_feature_unit_descriptor *desc = raw_desc;
|
||||
int nameid = uac_feature_unit_iFeature(desc);
|
||||
|
||||
__build_feature_ctl(state->mixer, state->map, ctl_mask, control,
|
||||
iterm, &state->oterm, unitid, nameid, readonly_mask);
|
||||
}
|
||||
|
||||
static void build_feature_ctl_badd(struct usb_mixer_interface *mixer,
|
||||
unsigned int ctl_mask, int control, int unitid,
|
||||
const struct usbmix_name_map *badd_map)
|
||||
{
|
||||
__build_feature_ctl(mixer, badd_map, ctl_mask, control,
|
||||
NULL, NULL, unitid, 0, 0);
|
||||
}
|
||||
|
||||
static void get_connector_control_name(struct mixer_build *state,
|
||||
struct usb_audio_term *term,
|
||||
bool is_input, char *name, int name_size)
|
||||
{
|
||||
int name_len = get_term_name(state, term, name, name_size, 0);
|
||||
int name_len = get_term_name(state->chip, term, name, name_size, 0);
|
||||
|
||||
if (name_len == 0)
|
||||
strlcpy(name, "Unknown", name_size);
|
||||
|
@ -1534,17 +1685,25 @@ static void build_connector_control(struct mixer_build *state,
|
|||
return;
|
||||
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id);
|
||||
/*
|
||||
* The first byte from reading the UAC2_TE_CONNECTOR control returns the
|
||||
* number of channels connected. This boolean ctl will simply report
|
||||
* if any channels are connected or not.
|
||||
* (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block)
|
||||
* UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the
|
||||
* number of channels connected.
|
||||
*
|
||||
* UAC3: The first byte specifies size of bitmap for the inserted controls. The
|
||||
* following byte(s) specifies which connectors are inserted.
|
||||
*
|
||||
* This boolean ctl will simply report if any channels are connected
|
||||
* or not.
|
||||
*/
|
||||
cval->control = UAC2_TE_CONNECTOR;
|
||||
if (state->mixer->protocol == UAC_VERSION_2)
|
||||
cval->control = UAC2_TE_CONNECTOR;
|
||||
else /* UAC_VERSION_3 */
|
||||
cval->control = UAC3_TE_INSERTION;
|
||||
|
||||
cval->val_type = USB_MIXER_BOOLEAN;
|
||||
cval->channels = 1; /* report true if any channel is connected */
|
||||
cval->min = 0;
|
||||
cval->max = 1;
|
||||
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
|
||||
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
|
||||
if (!kctl) {
|
||||
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
||||
kfree(cval);
|
||||
|
@ -1605,7 +1764,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
|||
}
|
||||
|
||||
kctl->private_free = snd_usb_mixer_elem_free;
|
||||
ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
|
||||
ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource,
|
||||
name, sizeof(name));
|
||||
if (ret > 0)
|
||||
snprintf(kctl->id.name, sizeof(kctl->id.name),
|
||||
|
@ -1692,7 +1851,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
|||
}
|
||||
|
||||
/* parse the source unit */
|
||||
if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
|
||||
err = parse_audio_unit(state, hdr->bSourceID);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* determine the input source type and name */
|
||||
|
@ -1806,16 +1966,15 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
|||
*/
|
||||
static void build_mixer_unit_ctl(struct mixer_build *state,
|
||||
struct uac_mixer_unit_descriptor *desc,
|
||||
int in_pin, int in_ch, int unitid,
|
||||
struct usb_audio_term *iterm)
|
||||
int in_pin, int in_ch, int num_outs,
|
||||
int unitid, struct usb_audio_term *iterm)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval;
|
||||
unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
|
||||
unsigned int i, len;
|
||||
struct snd_kcontrol *kctl;
|
||||
const struct usbmix_name_map *map;
|
||||
|
||||
map = find_map(state, unitid, 0);
|
||||
map = find_map(state->map, unitid, 0);
|
||||
if (check_ignored_ctl(map))
|
||||
return;
|
||||
|
||||
|
@ -1848,7 +2007,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
|
|||
|
||||
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
|
||||
if (!len)
|
||||
len = get_term_name(state, iterm, kctl->id.name,
|
||||
len = get_term_name(state->chip, iterm, kctl->id.name,
|
||||
sizeof(kctl->id.name), 0);
|
||||
if (!len)
|
||||
len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1);
|
||||
|
@ -1863,16 +2022,28 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
|
|||
void *raw_desc)
|
||||
{
|
||||
struct usb_audio_term iterm;
|
||||
struct uac2_input_terminal_descriptor *d = raw_desc;
|
||||
unsigned int control, bmctls, term_id;
|
||||
|
||||
check_input_term(state, d->bTerminalID, &iterm);
|
||||
if (state->mixer->protocol == UAC_VERSION_2) {
|
||||
/* Check for jack detection. */
|
||||
if (uac_v2v3_control_is_readable(le16_to_cpu(d->bmControls),
|
||||
UAC2_TE_CONNECTOR)) {
|
||||
build_connector_control(state, &iterm, true);
|
||||
}
|
||||
struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
|
||||
control = UAC2_TE_CONNECTOR;
|
||||
term_id = d_v2->bTerminalID;
|
||||
bmctls = le16_to_cpu(d_v2->bmControls);
|
||||
} else if (state->mixer->protocol == UAC_VERSION_3) {
|
||||
struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
|
||||
control = UAC3_TE_INSERTION;
|
||||
term_id = d_v3->bTerminalID;
|
||||
bmctls = le32_to_cpu(d_v3->bmControls);
|
||||
} else {
|
||||
return 0; /* UAC1. No Insertion control */
|
||||
}
|
||||
|
||||
check_input_term(state, term_id, &iterm);
|
||||
|
||||
/* Check for jack detection. */
|
||||
if (uac_v2v3_control_is_readable(bmctls, control))
|
||||
build_connector_control(state, &iterm, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1887,14 +2058,17 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
|||
int input_pins, num_ins, num_outs;
|
||||
int pin, ich, err;
|
||||
|
||||
if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
|
||||
!(num_outs = uac_mixer_unit_bNrChannels(desc))) {
|
||||
err = uac_mixer_unit_get_channels(state, desc);
|
||||
if (err < 0) {
|
||||
usb_audio_err(state->chip,
|
||||
"invalid MIXER UNIT descriptor %d\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
return err;
|
||||
}
|
||||
|
||||
num_outs = err;
|
||||
input_pins = desc->bNrInPins;
|
||||
|
||||
num_ins = 0;
|
||||
ich = 0;
|
||||
for (pin = 0; pin < input_pins; pin++) {
|
||||
|
@ -1921,7 +2095,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
|||
}
|
||||
}
|
||||
if (ich_has_controls)
|
||||
build_mixer_unit_ctl(state, desc, pin, ich,
|
||||
build_mixer_unit_ctl(state, desc, pin, ich, num_outs,
|
||||
unitid, &iterm);
|
||||
}
|
||||
}
|
||||
|
@ -2098,7 +2272,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
|||
}
|
||||
|
||||
for (i = 0; i < num_ins; i++) {
|
||||
if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
|
||||
err = parse_audio_unit(state, desc->baSourceID[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -2114,7 +2289,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
|||
|
||||
if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
|
||||
continue;
|
||||
map = find_map(state, unitid, valinfo->control);
|
||||
map = find_map(state->map, unitid, valinfo->control);
|
||||
if (check_ignored_ctl(map))
|
||||
continue;
|
||||
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
|
||||
|
@ -2162,7 +2337,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
|||
nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
|
||||
len = 0;
|
||||
if (nameid)
|
||||
len = snd_usb_copy_string_desc(state, nameid,
|
||||
len = snd_usb_copy_string_desc(state->chip,
|
||||
nameid,
|
||||
kctl->id.name,
|
||||
sizeof(kctl->id.name));
|
||||
if (!len)
|
||||
|
@ -2310,14 +2486,15 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
|||
}
|
||||
|
||||
for (i = 0; i < desc->bNrInPins; i++) {
|
||||
if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
|
||||
err = parse_audio_unit(state, desc->baSourceID[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (desc->bNrInPins == 1) /* only one ? nonsense! */
|
||||
return 0;
|
||||
|
||||
map = find_map(state, unitid, 0);
|
||||
map = find_map(state->map, unitid, 0);
|
||||
if (check_ignored_ctl(map))
|
||||
return 0;
|
||||
|
||||
|
@ -2358,7 +2535,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
|||
len = check_mapped_selector_name(state, unitid, i, namelist[i],
|
||||
MAX_ITEM_NAME_LEN);
|
||||
if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
|
||||
len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
|
||||
len = get_term_name(state->chip, &iterm, namelist[i],
|
||||
MAX_ITEM_NAME_LEN, 0);
|
||||
if (! len)
|
||||
sprintf(namelist[i], "Input %u", i);
|
||||
}
|
||||
|
@ -2380,12 +2558,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
|||
/* if iSelector is given, use it */
|
||||
nameid = uac_selector_unit_iSelector(desc);
|
||||
if (nameid)
|
||||
len = snd_usb_copy_string_desc(state, nameid,
|
||||
len = snd_usb_copy_string_desc(state->chip, nameid,
|
||||
kctl->id.name,
|
||||
sizeof(kctl->id.name));
|
||||
/* ... or pick up the terminal name at next */
|
||||
if (!len)
|
||||
len = get_term_name(state, &state->oterm,
|
||||
len = get_term_name(state->chip, &state->oterm,
|
||||
kctl->id.name, sizeof(kctl->id.name), 0);
|
||||
/* ... or use the fixed string "USB" as the last resort */
|
||||
if (!len)
|
||||
|
@ -2458,7 +2636,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
|||
} else { /* UAC_VERSION_3 */
|
||||
switch (p1[2]) {
|
||||
case UAC_INPUT_TERMINAL:
|
||||
return 0; /* NOP */
|
||||
return parse_audio_input_terminal(state, unitid, p1);
|
||||
case UAC3_MIXER_UNIT:
|
||||
return parse_audio_mixer_unit(state, unitid, p1);
|
||||
case UAC3_CLOCK_SOURCE:
|
||||
|
@ -2503,6 +2681,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* UAC3 predefined channels configuration */
|
||||
struct uac3_badd_profile {
|
||||
int subclass;
|
||||
const char *name;
|
||||
int c_chmask; /* capture channels mask */
|
||||
int p_chmask; /* playback channels mask */
|
||||
int st_chmask; /* side tone mixing channel mask */
|
||||
};
|
||||
|
||||
static struct uac3_badd_profile uac3_badd_profiles[] = {
|
||||
{
|
||||
/*
|
||||
* BAIF, BAOF or combination of both
|
||||
* IN: Mono or Stereo cfg, Mono alt possible
|
||||
* OUT: Mono or Stereo cfg, Mono alt possible
|
||||
*/
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
|
||||
.name = "GENERIC IO",
|
||||
.c_chmask = -1, /* dynamic channels */
|
||||
.p_chmask = -1, /* dynamic channels */
|
||||
},
|
||||
{
|
||||
/* BAOF; Stereo only cfg, Mono alt possible */
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
|
||||
.name = "HEADPHONE",
|
||||
.p_chmask = 3,
|
||||
},
|
||||
{
|
||||
/* BAOF; Mono or Stereo cfg, Mono alt possible */
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER,
|
||||
.name = "SPEAKER",
|
||||
.p_chmask = -1, /* dynamic channels */
|
||||
},
|
||||
{
|
||||
/* BAIF; Mono or Stereo cfg, Mono alt possible */
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
|
||||
.name = "MICROPHONE",
|
||||
.c_chmask = -1, /* dynamic channels */
|
||||
},
|
||||
{
|
||||
/*
|
||||
* BAIOF topology
|
||||
* IN: Mono only
|
||||
* OUT: Mono or Stereo cfg, Mono alt possible
|
||||
*/
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_HEADSET,
|
||||
.name = "HEADSET",
|
||||
.c_chmask = 1,
|
||||
.p_chmask = -1, /* dynamic channels */
|
||||
.st_chmask = 1,
|
||||
},
|
||||
{
|
||||
/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
|
||||
.name = "HEADSET ADAPTER",
|
||||
.c_chmask = 1,
|
||||
.p_chmask = 3,
|
||||
.st_chmask = 1,
|
||||
},
|
||||
{
|
||||
/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
|
||||
.subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
|
||||
.name = "SPEAKERPHONE",
|
||||
.c_chmask = 1,
|
||||
.p_chmask = 1,
|
||||
},
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer,
|
||||
struct uac3_badd_profile *f,
|
||||
int c_chmask, int p_chmask)
|
||||
{
|
||||
/*
|
||||
* If both playback/capture channels are dynamic, make sure
|
||||
* at least one channel is present
|
||||
*/
|
||||
if (f->c_chmask < 0 && f->p_chmask < 0) {
|
||||
if (!c_chmask && !p_chmask) {
|
||||
usb_audio_warn(mixer->chip, "BAAD %s: no channels?",
|
||||
f->name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((f->c_chmask < 0 && !c_chmask) ||
|
||||
(f->c_chmask >= 0 && f->c_chmask != c_chmask)) {
|
||||
usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch",
|
||||
f->name);
|
||||
return false;
|
||||
}
|
||||
if ((f->p_chmask < 0 && !p_chmask) ||
|
||||
(f->p_chmask >= 0 && f->p_chmask != p_chmask)) {
|
||||
usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch",
|
||||
f->name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* create mixer controls for UAC3 BADD profiles
|
||||
*
|
||||
* UAC3 BADD device doesn't contain CS descriptors thus we will guess everything
|
||||
*
|
||||
* BADD device may contain Mixer Unit, which doesn't have any controls, skip it
|
||||
*/
|
||||
static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
|
||||
int ctrlif)
|
||||
{
|
||||
struct usb_device *dev = mixer->chip->dev;
|
||||
struct usb_interface_assoc_descriptor *assoc;
|
||||
int badd_profile = mixer->chip->badd_profile;
|
||||
struct uac3_badd_profile *f;
|
||||
const struct usbmix_ctl_map *map;
|
||||
int p_chmask = 0, c_chmask = 0, st_chmask = 0;
|
||||
int i;
|
||||
|
||||
assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
||||
|
||||
/* Detect BADD capture/playback channels from AS EP descriptors */
|
||||
for (i = 0; i < assoc->bInterfaceCount; i++) {
|
||||
int intf = assoc->bFirstInterface + i;
|
||||
|
||||
struct usb_interface *iface;
|
||||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
unsigned int maxpacksize;
|
||||
char dir_in;
|
||||
int chmask, num;
|
||||
|
||||
if (intf == ctrlif)
|
||||
continue;
|
||||
|
||||
iface = usb_ifnum_to_if(dev, intf);
|
||||
num = iface->num_altsetting;
|
||||
|
||||
if (num < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The number of Channels in an AudioStreaming interface
|
||||
* and the audio sample bit resolution (16 bits or 24
|
||||
* bits) can be derived from the wMaxPacketSize field in
|
||||
* the Standard AS Audio Data Endpoint descriptor in
|
||||
* Alternate Setting 1
|
||||
*/
|
||||
alts = &iface->altsetting[1];
|
||||
altsd = get_iface_desc(alts);
|
||||
|
||||
if (altsd->bNumEndpoints < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* check direction */
|
||||
dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN);
|
||||
maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
||||
|
||||
switch (maxpacksize) {
|
||||
default:
|
||||
usb_audio_err(mixer->chip,
|
||||
"incorrect wMaxPacketSize 0x%x for BADD profile\n",
|
||||
maxpacksize);
|
||||
return -EINVAL;
|
||||
case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
|
||||
case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
|
||||
case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
|
||||
case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
|
||||
chmask = 1;
|
||||
break;
|
||||
case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
|
||||
case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
|
||||
case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
|
||||
case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
|
||||
chmask = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dir_in)
|
||||
c_chmask = chmask;
|
||||
else
|
||||
p_chmask = chmask;
|
||||
}
|
||||
|
||||
usb_audio_dbg(mixer->chip,
|
||||
"UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n",
|
||||
badd_profile, c_chmask, p_chmask);
|
||||
|
||||
/* check the mapping table */
|
||||
for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) {
|
||||
if (map->id == badd_profile)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!map->id)
|
||||
return -EINVAL;
|
||||
|
||||
for (f = uac3_badd_profiles; f->name; f++) {
|
||||
if (badd_profile == f->subclass)
|
||||
break;
|
||||
}
|
||||
if (!f->name)
|
||||
return -EINVAL;
|
||||
if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask))
|
||||
return -EINVAL;
|
||||
st_chmask = f->st_chmask;
|
||||
|
||||
/* Playback */
|
||||
if (p_chmask) {
|
||||
/* Master channel, always writable */
|
||||
build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
|
||||
UAC3_BADD_FU_ID2, map->map);
|
||||
/* Mono/Stereo volume channels, always writable */
|
||||
build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME,
|
||||
UAC3_BADD_FU_ID2, map->map);
|
||||
}
|
||||
|
||||
/* Capture */
|
||||
if (c_chmask) {
|
||||
/* Master channel, always writable */
|
||||
build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
|
||||
UAC3_BADD_FU_ID5, map->map);
|
||||
/* Mono/Stereo volume channels, always writable */
|
||||
build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME,
|
||||
UAC3_BADD_FU_ID5, map->map);
|
||||
}
|
||||
|
||||
/* Side tone-mixing */
|
||||
if (st_chmask) {
|
||||
/* Master channel, always writable */
|
||||
build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
|
||||
UAC3_BADD_FU_ID7, map->map);
|
||||
/* Mono volume channel, always writable */
|
||||
build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME,
|
||||
UAC3_BADD_FU_ID7, map->map);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create mixer controls
|
||||
*
|
||||
|
@ -2596,6 +3014,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
|||
err = parse_audio_unit(&state, desc->bCSourceID);
|
||||
if (err < 0 && err != -EINVAL)
|
||||
return err;
|
||||
|
||||
if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
|
||||
UAC3_TE_INSERTION)) {
|
||||
build_connector_control(&state, &state.oterm,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2606,9 +3030,9 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
|
|||
{
|
||||
struct usb_mixer_elem_list *list;
|
||||
|
||||
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
|
||||
for_each_mixer_elem(list, mixer, unitid) {
|
||||
struct usb_mixer_elem_info *info =
|
||||
(struct usb_mixer_elem_info *)list;
|
||||
mixer_elem_list_to_info(list);
|
||||
/* invalidate cache, so the value is read from the device */
|
||||
info->cached = 0;
|
||||
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
|
@ -2619,7 +3043,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
|
|||
static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
|
||||
struct usb_mixer_elem_list *list)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
|
||||
struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list);
|
||||
static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN",
|
||||
"S8", "U8", "S16", "U16"};
|
||||
snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, "
|
||||
|
@ -2645,8 +3069,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
|
|||
mixer->ignore_ctl_error);
|
||||
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
|
||||
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
|
||||
for (list = mixer->id_elems[unitid]; list;
|
||||
list = list->next_id_elem) {
|
||||
for_each_mixer_elem(list, mixer, unitid) {
|
||||
snd_iprintf(buffer, " Unit: %i\n", list->id);
|
||||
if (list->kctl)
|
||||
snd_iprintf(buffer,
|
||||
|
@ -2676,19 +3099,19 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
|
|||
return;
|
||||
}
|
||||
|
||||
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
|
||||
for_each_mixer_elem(list, mixer, unitid)
|
||||
count++;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
|
||||
for_each_mixer_elem(list, mixer, unitid) {
|
||||
struct usb_mixer_elem_info *info;
|
||||
|
||||
if (!list->kctl)
|
||||
continue;
|
||||
|
||||
info = (struct usb_mixer_elem_info *)list;
|
||||
info = mixer_elem_list_to_info(list);
|
||||
if (count > 1 && info->control != control)
|
||||
continue;
|
||||
|
||||
|
@ -2809,6 +3232,48 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] = mixer->chip->keep_iface;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
|
||||
bool keep_iface = !!ucontrol->value.integer.value[0];
|
||||
|
||||
if (mixer->chip->keep_iface == keep_iface)
|
||||
return 0;
|
||||
mixer->chip->keep_iface = keep_iface;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new keep_iface_ctl = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
|
||||
.name = "Keep Interface",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = keep_iface_ctl_get,
|
||||
.put = keep_iface_ctl_put,
|
||||
};
|
||||
|
||||
static int create_keep_iface_ctl(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer);
|
||||
|
||||
/* need only one control per card */
|
||||
if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) {
|
||||
snd_ctl_free_one(kctl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return snd_ctl_add(mixer->chip->card, kctl);
|
||||
}
|
||||
|
||||
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
||||
int ignore_error)
|
||||
{
|
||||
|
@ -2847,8 +3312,21 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
|||
break;
|
||||
}
|
||||
|
||||
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
|
||||
(err = snd_usb_mixer_status_create(mixer)) < 0)
|
||||
if (mixer->protocol == UAC_VERSION_3 &&
|
||||
chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
|
||||
err = snd_usb_mixer_controls_badd(mixer, ctrlif);
|
||||
if (err < 0)
|
||||
goto _error;
|
||||
} else {
|
||||
err = snd_usb_mixer_controls(mixer);
|
||||
if (err < 0)
|
||||
goto _error;
|
||||
err = snd_usb_mixer_status_create(mixer);
|
||||
if (err < 0)
|
||||
goto _error;
|
||||
}
|
||||
err = create_keep_iface_ctl(mixer);
|
||||
if (err < 0)
|
||||
goto _error;
|
||||
|
||||
snd_usb_mixer_apply_create_quirk(mixer);
|
||||
|
@ -2909,7 +3387,7 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
|
|||
|
||||
static int restore_mixer_value(struct usb_mixer_elem_list *list)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
|
||||
struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list);
|
||||
int c, err, idx;
|
||||
|
||||
if (cval->cmask) {
|
||||
|
@ -2945,8 +3423,7 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
|
|||
if (reset_resume) {
|
||||
/* restore cached mixer values */
|
||||
for (id = 0; id < MAX_ID_ELEMS; id++) {
|
||||
for (list = mixer->id_elems[id]; list;
|
||||
list = list->next_id_elem) {
|
||||
for_each_mixer_elem(list, mixer, id) {
|
||||
if (list->resume) {
|
||||
err = list->resume(list);
|
||||
if (err < 0)
|
||||
|
@ -2956,6 +3433,8 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
|
|||
}
|
||||
}
|
||||
|
||||
snd_usb_mixer_resume_quirk(mixer);
|
||||
|
||||
return snd_usb_mixer_activate(mixer);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -53,6 +53,12 @@ struct usb_mixer_elem_list {
|
|||
usb_mixer_elem_resume_func_t resume;
|
||||
};
|
||||
|
||||
/* iterate over mixer element list of the given unit id */
|
||||
#define for_each_mixer_elem(list, mixer, id) \
|
||||
for ((list) = (mixer)->id_elems[id]; (list); (list) = (list)->next_id_elem)
|
||||
#define mixer_elem_list_to_info(list) \
|
||||
container_of(list, struct usb_mixer_elem_info, head)
|
||||
|
||||
struct usb_mixer_elem_info {
|
||||
struct usb_mixer_elem_list head;
|
||||
unsigned int control; /* CS or ICN (high byte) */
|
||||
|
|
|
@ -485,3 +485,68 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
|
|||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
/*
|
||||
* Control map entries for UAC3 BADD profiles
|
||||
*/
|
||||
|
||||
static struct usbmix_name_map uac3_badd_generic_io_map[] = {
|
||||
{ UAC3_BADD_FU_ID2, "Generic Out Playback" },
|
||||
{ UAC3_BADD_FU_ID5, "Generic In Capture" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
static struct usbmix_name_map uac3_badd_headphone_map[] = {
|
||||
{ UAC3_BADD_FU_ID2, "Headphone Playback" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
static struct usbmix_name_map uac3_badd_speaker_map[] = {
|
||||
{ UAC3_BADD_FU_ID2, "Speaker Playback" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
static struct usbmix_name_map uac3_badd_microphone_map[] = {
|
||||
{ UAC3_BADD_FU_ID5, "Mic Capture" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
/* Covers also 'headset adapter' profile */
|
||||
static struct usbmix_name_map uac3_badd_headset_map[] = {
|
||||
{ UAC3_BADD_FU_ID2, "Headset Playback" },
|
||||
{ UAC3_BADD_FU_ID5, "Headset Capture" },
|
||||
{ UAC3_BADD_FU_ID7, "Sidetone Mixing" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
static struct usbmix_name_map uac3_badd_speakerphone_map[] = {
|
||||
{ UAC3_BADD_FU_ID2, "Speaker Playback" },
|
||||
{ UAC3_BADD_FU_ID5, "Mic Capture" },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = {
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
|
||||
.map = uac3_badd_generic_io_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
|
||||
.map = uac3_badd_headphone_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_SPEAKER,
|
||||
.map = uac3_badd_speaker_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
|
||||
.map = uac3_badd_microphone_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_HEADSET,
|
||||
.map = uac3_badd_headset_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
|
||||
.map = uac3_badd_headset_map,
|
||||
},
|
||||
{
|
||||
.id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
|
||||
.map = uac3_badd_speakerphone_map,
|
||||
},
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
|
|
@ -1172,7 +1172,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
|
|||
int unitid = 12; /* SamleRate ExtensionUnit ID */
|
||||
|
||||
list_for_each_entry(mixer, &chip->mixer_list, list) {
|
||||
cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid];
|
||||
cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
|
||||
if (cval) {
|
||||
snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
|
||||
cval->control << 8,
|
||||
|
@ -1799,12 +1799,33 @@ static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
|
|||
NULL);
|
||||
}
|
||||
|
||||
static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
|
||||
{
|
||||
u16 buf = 0;
|
||||
|
||||
snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
||||
ch, snd_usb_ctrl_intf(chip) | (id << 8),
|
||||
&buf, 2);
|
||||
}
|
||||
|
||||
static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
/* fix to 0dB playback volumes */
|
||||
dell_dock_init_vol(mixer->chip, 1, 16);
|
||||
dell_dock_init_vol(mixer->chip, 2, 16);
|
||||
dell_dock_init_vol(mixer->chip, 1, 19);
|
||||
dell_dock_init_vol(mixer->chip, 2, 19);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
int err = 0;
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
|
||||
err = snd_usb_soundblaster_remote_init(mixer);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (mixer->chip->usb_id) {
|
||||
|
@ -1884,11 +1905,25 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|||
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
|
||||
err = snd_soundblaster_e1_switch_create(mixer);
|
||||
break;
|
||||
case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
|
||||
err = dell_dock_mixer_init(mixer);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
switch (mixer->chip->usb_id) {
|
||||
case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
|
||||
dell_dock_mixer_init(mixer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
|
||||
int unitid)
|
||||
{
|
||||
|
|
|
@ -14,5 +14,9 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
|
|||
struct usb_mixer_elem_info *cval, int unitid,
|
||||
struct snd_kcontrol *kctl);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer);
|
||||
#endif
|
||||
|
||||
#endif /* SND_USB_MIXER_QUIRKS_H */
|
||||
|
||||
|
|
|
@ -287,8 +287,7 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl,
|
|||
|
||||
static int scarlett_ctl_resume(struct usb_mixer_elem_list *list)
|
||||
{
|
||||
struct usb_mixer_elem_info *elem =
|
||||
container_of(list, struct usb_mixer_elem_info, head);
|
||||
struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < elem->channels; i++)
|
||||
|
@ -447,8 +446,7 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl,
|
|||
|
||||
static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list)
|
||||
{
|
||||
struct usb_mixer_elem_info *elem =
|
||||
container_of(list, struct usb_mixer_elem_info, head);
|
||||
struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
|
||||
|
||||
if (elem->cached)
|
||||
snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue