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:
Takashi Iwai 2018-06-04 11:41:48 +02:00
commit cdbc653a04
129 changed files with 8718 additions and 1444 deletions

View file

@ -96,6 +96,8 @@ source "sound/x86/Kconfig"
source "sound/synth/Kconfig"
source "sound/xen/Kconfig"
endif # SND
endif # !UML

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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)

View file

@ -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(&register_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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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>");

View file

@ -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 */

View file

@ -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");

View file

@ -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;

View file

@ -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

View 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, &reg,
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;
}

View 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,
&reg, 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;
}

View file

@ -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.
*/

View file

@ -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,
&reg, sizeof(reg));
if (err < 0)
return err;
midi_in_ports = be32_to_cpu(reg);
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
&reg, 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;

View 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;
}

View file

@ -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,
&reg, sizeof(reg));
} else {
stream = &dice->rx_stream[index];
err = snd_dice_transaction_read_rx(dice,
size * index + RX_NUMBER_AUDIO,
&reg, 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, &reg,
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, &reg,
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);

View file

@ -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);
}

View file

@ -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,
&reg, 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;

View 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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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 */

View file

@ -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);

View file

@ -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++) {

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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 *);

View file

@ -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;

View file

@ -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);

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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))

View file

@ -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 */

View file

@ -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);

View 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;
}
}

View file

@ -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

View file

@ -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" },
{}
};

View file

@ -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;

View file

@ -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},

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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))

View file

@ -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;
}

View file

@ -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);

View file

@ -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},

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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)
{

View file

@ -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

View file

@ -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) */

View file

@ -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 */
};

View file

@ -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)
{

View file

@ -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 */

View file

@ -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